├── .gitattributes ├── .gitignore ├── Diagram.png ├── LICENSE ├── README.md ├── Revalee.Client.Mvc ├── CallbackActionAttribute.cs ├── CallbackStateCache.cs ├── License.txt ├── Properties │ └── AssemblyInfo.cs ├── Revalee.Client.Mvc.csproj ├── RevaleeClientSettingsAttribute.cs ├── RevaleeControllerExtensions.cs ├── SchedulingAgent.cs └── packages.config ├── Revalee.Client ├── AsyncRevaleeRegistrar.cs ├── Configuration │ ├── ClientSettingsConfigurationElement.cs │ ├── RevaleeSection.cs │ └── UrlValidatorAttribute.cs ├── License.txt ├── Properties │ └── AssemblyInfo.cs ├── RecurringTasks │ ├── ActivationFailureEventArgs.cs │ ├── ActivationState.cs │ ├── CallbackRequest.cs │ ├── ConfiguredTask.cs │ ├── DeactivationEventArgs.cs │ ├── EnumConfigurationConverter.cs │ ├── IClockSource.cs │ ├── IRecurringTask.cs │ ├── ITaskCollection.cs │ ├── ITaskManifest.cs │ ├── ImmutableTaskCollection.cs │ ├── PeriodicityType.cs │ ├── RecurringTaskCallbackDetails.cs │ ├── RecurringTaskModule.cs │ ├── RequestAnalysis.cs │ ├── SystemClock.cs │ ├── TaskBuilder.cs │ ├── TaskConfigurationCollection.cs │ ├── TaskConfigurationElement.cs │ └── TaskManifest.cs ├── Revalee.Client.csproj ├── RevaleeClientSettings.cs ├── RevaleeRegistrar.cs ├── RevaleeRequestException.cs ├── ServiceBaseUri.cs └── Validation │ ├── AuthorizationCipher.cs │ └── RequestValidator.cs ├── Revalee.SampleSite ├── App_Start │ └── RouteConfig.cs ├── Content │ └── Site.css ├── Controllers │ └── RevaleeTestController.cs ├── Global.asax ├── Global.asax.cs ├── Infrastructure │ ├── CallbackDetails.cs │ └── CallbackLog.cs ├── Properties │ └── AssemblyInfo.cs ├── Revalee.SampleSite.csproj ├── Views │ ├── RevaleeTest │ │ ├── Index.cshtml │ │ └── ViewLog.cshtml │ └── Web.config ├── Web.config ├── favicon.ico └── packages.config ├── Revalee.Service ├── AbortableThreadPool.cs ├── App.config ├── ApplicationFolderHelper.cs ├── AuthorizationHelper.cs ├── CancelRequestHandler.cs ├── CommandLineInstaller.cs ├── CommandRouter.cs ├── ConfigurationManager.cs ├── EsePersistence │ ├── EseTaskPersistenceProvider.cs │ ├── Esent.Interop.dll │ ├── Esent.Interop.pdb │ └── Esent.Interop.xml ├── ILoggingProvider.cs ├── IPNetwork.cs ├── IPartialMatchDictionary.cs ├── IRequestHandler.cs ├── ITaskPersistenceProvider.cs ├── ITelemetryAdapter.cs ├── Installation Notes.txt ├── InteractiveExecution.cs ├── License.txt ├── ListenerPrefix.cs ├── NullTaskPersistenceProvider.cs ├── NullTelemetryAdapter.cs ├── PerformanceCounterTelemetryAdapter.cs ├── Properties │ └── AssemblyInfo.cs ├── RequestHandlerDirectory.cs ├── RequestHandlerMapping.cs ├── RequestManager.cs ├── RetryHeuristics.cs ├── Revalee.Designer.cs ├── Revalee.Service.csproj ├── Revalee.cs ├── Revalee.ico ├── Revalee.resx ├── RevaleeServiceInstaller.Designer.cs ├── RevaleeServiceInstaller.cs ├── RevaleeServiceInstaller.resx ├── RevaleeTask.cs ├── RevaleeUrlAuthorization.cs ├── RollingLogFileTraceListener.cs ├── ScheduleRequestHandler.cs ├── ScheduledDictionary.cs ├── SecuritySettingsConfigSection.cs ├── StateManager.cs ├── StatusRequestHandler.cs ├── Supervisor.cs ├── TaskExporter.cs ├── TaskPersistenceProviderMapping.cs ├── TaskPersistenceSettings.cs ├── TelemetryManager.cs ├── TimeManager.cs ├── TraceListenerLoggingProvider.cs ├── UrlAuthorizationElement.cs ├── UrlAuthorizationElementCollection.cs ├── UrlMatchDictionary.cs └── WorkManager.cs ├── Revalee.sln ├── Revalee.snk └── packages ├── Microsoft.AspNet.Mvc.5.1.1 ├── Content │ ├── Web.config.install.xdt │ └── Web.config.uninstall.xdt ├── Microsoft.AspNet.Mvc.5.1.1.nupkg ├── Microsoft.AspNet.Mvc.5.1.1.nuspec └── lib │ └── net45 │ ├── System.Web.Mvc.dll │ └── System.Web.Mvc.xml ├── Microsoft.AspNet.Razor.3.1.1 ├── Microsoft.AspNet.Razor.3.1.1.nupkg ├── Microsoft.AspNet.Razor.3.1.1.nuspec └── lib │ └── net45 │ ├── System.Web.Razor.dll │ └── System.Web.Razor.xml ├── Microsoft.AspNet.WebPages.3.1.1 ├── Content │ ├── Web.config.install.xdt │ └── Web.config.uninstall.xdt ├── Microsoft.AspNet.WebPages.3.1.1.nupkg ├── Microsoft.AspNet.WebPages.3.1.1.nuspec └── lib │ └── net45 │ ├── System.Web.Helpers.dll │ ├── System.Web.Helpers.xml │ ├── System.Web.WebPages.Deployment.dll │ ├── System.Web.WebPages.Deployment.xml │ ├── System.Web.WebPages.Razor.dll │ ├── System.Web.WebPages.Razor.xml │ ├── System.Web.WebPages.dll │ └── System.Web.WebPages.xml ├── Microsoft.Web.Infrastructure.1.0.0.0 ├── Microsoft.Web.Infrastructure.1.0.0.0.nupkg ├── Microsoft.Web.Infrastructure.1.0.0.0.nuspec └── lib │ └── net40 │ └── Microsoft.Web.Infrastructure.dll └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | # Catch all for anything we forgot. Add rules if you get CRLF to LF warnings. 2 | * text=auto 3 | 4 | # Text files that should be normalized to LF in odb. 5 | *.cs text diff=csharp 6 | *.config text 7 | 8 | *.sln text 9 | *.csproj text 10 | 11 | *.md text 12 | *.tt text 13 | *.sh text 14 | *.ps1 text 15 | *.cmd text 16 | *.bat text 17 | *.markdown text 18 | *.md text 19 | *.msbuild text 20 | 21 | 22 | # Binary files that should not be normalized or diffed 23 | *.png binary 24 | *.jpg binary 25 | *.gif binary 26 | *.ico binary 27 | *.rc binary 28 | 29 | *.pfx binary 30 | *.snk binary 31 | *.dll binary 32 | *.exe binary 33 | *.lib binary 34 | *.exp binary 35 | *.pdb binary 36 | *.sdf binary 37 | *.zip binary -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | 4 | # Folder config file 5 | Desktop.ini 6 | 7 | # Files left over from merge conflicts 8 | *.orig 9 | 10 | ## Ignore Visual Studio temporary files, build results, and 11 | ## files generated by popular Visual Studio add-ons. 12 | 13 | # Visual Studio hidden folder 14 | .vs/ 15 | 16 | # User-specific files 17 | slnx.sqlite 18 | *.suo 19 | *.user 20 | 21 | # Build results 22 | bin/[Dd]ebug/ 23 | bin/[Rr]elease/ 24 | *.obj 25 | bin/**/*.pdb 26 | *.tmp 27 | *.vspscc 28 | 29 | # Visual Studio profiler 30 | *.psess 31 | *.vsp 32 | 33 | # Click-Once directory 34 | publish 35 | 36 | # Others 37 | [Bb]in 38 | [Oo]bj 39 | sql 40 | TestResults 41 | *.Cache 42 | ClientBin 43 | stylecop.* 44 | ~$* 45 | *.userprefs -------------------------------------------------------------------------------- /Diagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/Diagram.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sage Analytic Technologies, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Revalee 2 | ======= 3 | 4 | Scheduled callbacks for your web applications 5 | 6 | ![Workflow Diagram](Diagram.png?raw=true) 7 | 8 | Rationale 9 | --------- 10 | 11 | Revalee can simplify your web application's workflow when events are required AFTER the normal handling of a web request. You can write all of your scheduled activities in the same toolset and application as the rest of your web code. No more command line utilities or custom batch jobs. 12 | 13 | Here are some common ways to utilize the power of scheduled callbacks: 14 | 15 | * Sending reminder email messages. 16 | * Running recurring automated reports. 17 | * Scheduling daily maintenance functions. 18 | * Expiring accounts or subscriptions. 19 | * Canceling incomplete transactions. 20 | * Consolidating multiple notification messages. 21 | * Purging temporary files. 22 | 23 | Getting Started 24 | --------------- 25 | 26 | Revalee consists of a Windows service that stores and initiates the callbacks, plus a client API to schedule those callbacks. You will need to install both service and client to get started. 27 | 28 | **Windows Service** 29 | 30 | The Revalee service is available on [Chocolatey](http://chocolatey.org). To perform a quick install, use: 31 | 32 | ``` 33 | cinst Revalee.Service 34 | ``` 35 | 36 | The Revalee service can also be downloaded [here](http://revalee.sageanalytic.com#Download) or built from the source code. 37 | 38 | **Client Library** 39 | 40 | The Revalee client library is available on [NuGet](http://www.nuget.org). To include in a .NET 4.0+ project, use: 41 | 42 | ``` 43 | Install-Package Revalee.Client 44 | ``` 45 | 46 | 47 | Alternatively for ASP.NET MVC projects targeting .NET 4.5+, a specialized Revalee client library is also available on NuGet. To install: 48 | 49 | ``` 50 | Install-Package Revalee.Client.Mvc 51 | ``` 52 | 53 | Callbacks can also be scheduled from any platform without the use of a client library by using the REST API. 54 | 55 | Usage Examples 56 | -------------- 57 | 58 | Request a one-time web callback in 1 hour for id=123456 59 | 60 | ```c# 61 | var callbackDelay = TimeSpan.FromHours(1.0); 62 | var callbackUri = new Uri("http://localhost/Home/Callback?id=123456"); 63 | 64 | Guid callbackId = RevaleeRegistrar.ScheduleCallback(callbackDelay, callbackUri); 65 | ``` 66 | 67 | Request a recurring web callback every night at 3:30 AM 68 | 69 | ```c# 70 | var manifest = RecurringTasks.RecurringTaskModule.GetManifest(); 71 | var callbackUri = new Uri("http://localhost/Recurring/Nightly"); 72 | 73 | manifest.AddDailyTask(3, 30, callbackUri); 74 | ``` 75 | 76 | [Full API Reference](http://revalee.sageanalytic.com/Help/index.html) 77 | 78 | Supported Platforms 79 | ------------------- 80 | * Windows XP or later 81 | 82 | Client library 83 | 84 | * .NET Framework 4.0+ (only System, System.Core, System.Web assemblies are referenced) 85 | 86 | The ASP.NET MVC client library requires .NET Framework 4.5+ and Microsoft ASP.NET MVC 5+. 87 | 88 | 89 | More information is available at the [Revalee Project Site](http://revalee.sageanalytic.com). 90 | -------------------------------------------------------------------------------- /Revalee.Client.Mvc/CallbackActionAttribute.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Web.Mvc; 31 | 32 | namespace Revalee.Client.Mvc 33 | { 34 | /// 35 | /// Represents an attribute that will restrict access to previously requested callbacks. 36 | /// 37 | [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = false)] 38 | public sealed class CallbackActionAttribute : ActionFilterAttribute, IAuthorizationFilter 39 | { 40 | /// 41 | /// Called when a process requests authorization for the marked callback action. 42 | /// 43 | /// The filter context, which encapsulates information for using . 44 | /// The parameter is null. 45 | public void OnAuthorization(AuthorizationContext filterContext) 46 | { 47 | if (filterContext == null) 48 | { 49 | throw new ArgumentNullException("filterContext"); 50 | } 51 | 52 | if (filterContext.HttpContext == null 53 | || filterContext.HttpContext.Request == null 54 | || !RevaleeRegistrar.ValidateCallback(filterContext.HttpContext.Request)) 55 | { 56 | filterContext.Result = new HttpUnauthorizedResult(); 57 | } 58 | } 59 | 60 | /// 61 | /// Called before the action executes to supply cached state information if present. 62 | /// 63 | /// The filter context, which encapsulates information for using . 64 | /// The parameter is null. 65 | public override void OnActionExecuting(ActionExecutingContext filterContext) 66 | { 67 | base.OnActionExecuting(filterContext); 68 | 69 | if (filterContext == null) 70 | { 71 | throw new ArgumentNullException("filterContext"); 72 | } 73 | 74 | const string callbackIdFormParameterName = "callbackId"; 75 | const string stateActionParameterName = "state"; 76 | 77 | if (filterContext.HttpContext != null 78 | && filterContext.HttpContext.Request != null 79 | && filterContext.ActionParameters != null 80 | && filterContext.ActionDescriptor != null 81 | ) 82 | { 83 | string callbackId = filterContext.HttpContext.Request.Form[callbackIdFormParameterName]; 84 | 85 | if (!string.IsNullOrEmpty(callbackId)) 86 | { 87 | if (filterContext.ActionParameters.ContainsKey(stateActionParameterName)) 88 | { 89 | object cachedState = CallbackStateCache.RecoverCallbackState(filterContext.HttpContext, callbackId); 90 | 91 | if (cachedState != null) 92 | { 93 | foreach (ParameterDescriptor stateParameter in filterContext.ActionDescriptor.GetParameters()) 94 | { 95 | if (string.Equals(stateParameter.ParameterName, stateActionParameterName, StringComparison.OrdinalIgnoreCase) 96 | && stateParameter.ParameterType.IsAssignableFrom(cachedState.GetType())) 97 | { 98 | filterContext.ActionParameters[stateActionParameterName] = cachedState; 99 | return; 100 | } 101 | } 102 | } 103 | } 104 | } 105 | } 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /Revalee.Client.Mvc/CallbackStateCache.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web; 3 | 4 | namespace Revalee.Client.Mvc 5 | { 6 | internal static class CallbackStateCache 7 | { 8 | internal static void StoreCallbackState(HttpContextBase context, Guid callbackId, object state, DateTime expirationTime) 9 | { 10 | if (context == null) 11 | { 12 | throw new ArgumentNullException("context"); 13 | } 14 | 15 | if (Guid.Empty.Equals(callbackId)) 16 | { 17 | throw new ArgumentNullException("callbackId"); 18 | } 19 | 20 | if (context.Cache == null) 21 | { 22 | return; 23 | } 24 | 25 | if (state == null) 26 | { 27 | RemoveFromCache(context, callbackId.ToString()); 28 | return; 29 | } 30 | 31 | AddToCache(context, callbackId.ToString(), state, expirationTime); 32 | } 33 | 34 | internal static void StoreCallbackState(HttpContextBase context, Guid callbackId, object state, TimeSpan expirationTimeSpan) 35 | { 36 | if (context == null) 37 | { 38 | throw new ArgumentNullException("context"); 39 | } 40 | 41 | if (Guid.Empty.Equals(callbackId)) 42 | { 43 | throw new ArgumentNullException("callbackId"); 44 | } 45 | 46 | if (context.Cache == null) 47 | { 48 | return; 49 | } 50 | 51 | if (state == null) 52 | { 53 | RemoveFromCache(context, callbackId.ToString()); 54 | return; 55 | } 56 | 57 | AddToCache(context, callbackId.ToString(), state, DateTime.Now.Add(expirationTimeSpan)); 58 | } 59 | 60 | internal static void DeleteCallbackState(HttpContextBase context, Guid callbackId) 61 | { 62 | if (context == null) 63 | { 64 | throw new ArgumentNullException("context"); 65 | } 66 | 67 | if (context.Cache == null) 68 | { 69 | return; 70 | } 71 | 72 | RemoveFromCache(context, callbackId.ToString()); 73 | } 74 | 75 | internal static object RecoverCallbackState(HttpContextBase context, Guid callbackId) 76 | { 77 | if (context == null) 78 | { 79 | throw new ArgumentNullException("context"); 80 | } 81 | 82 | if (context.Cache == null) 83 | { 84 | return null; 85 | } 86 | 87 | return RemoveFromCache(context, callbackId.ToString()); 88 | } 89 | 90 | internal static object RecoverCallbackState(HttpContextBase context, string cacheKey) 91 | { 92 | if (context == null) 93 | { 94 | throw new ArgumentNullException("context"); 95 | } 96 | 97 | if (cacheKey == null) 98 | { 99 | throw new ArgumentNullException("cacheKey"); 100 | } 101 | 102 | if (context.Cache == null) 103 | { 104 | return null; 105 | } 106 | 107 | return RemoveFromCache(context, cacheKey); 108 | } 109 | 110 | private static void AddToCache(HttpContextBase context, string cacheKey, object state, DateTime expirationTime) 111 | { 112 | context.Cache.Insert(cacheKey, state, null, expirationTime, System.Web.Caching.Cache.NoSlidingExpiration, System.Web.Caching.CacheItemPriority.High, null); 113 | } 114 | 115 | private static object RemoveFromCache(HttpContextBase context, string cacheKey) 116 | { 117 | return context.Cache.Remove(cacheKey); 118 | } 119 | } 120 | } -------------------------------------------------------------------------------- /Revalee.Client.Mvc/License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sage Analytic Technologies, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Revalee.Client.Mvc/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Revalee.Client.Mvc")] 6 | [assembly: AssemblyDescription("ASP.NET MVC client library for scheduled web callbacks.")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("Sage Analytic")] 9 | [assembly: AssemblyProduct("Revalee.Client.Mvc")] 10 | [assembly: AssemblyCopyright("Copyright © 2014, Sage Analytic Technologies, LLC")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | [assembly: CLSCompliant(true)] 14 | [assembly: ComVisible(false)] 15 | [assembly: Guid("8e32e519-813c-450a-9761-39e2be6a2e40")] 16 | [assembly: AssemblyVersion("2.0.0")] 17 | [assembly: AssemblyFileVersion("2.3.1")] 18 | [assembly: AssemblyInformationalVersion("2.3.1")] -------------------------------------------------------------------------------- /Revalee.Client.Mvc/Revalee.Client.Mvc.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {AC55639C-BFCF-496C-9019-D3D911706699} 8 | Library 9 | Properties 10 | Revalee.Client.Mvc 11 | Revalee.Client.Mvc 12 | v4.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | bin\Debug\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | AllRules.ruleset 24 | 25 | 26 | none 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | bin\Release\Revalee.Client.Mvc.xml 33 | 34 | 35 | true 36 | 37 | 38 | ..\Revalee.snk 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | False 48 | ..\packages\Microsoft.AspNet.WebPages.3.1.1\lib\net45\System.Web.Helpers.dll 49 | 50 | 51 | False 52 | ..\packages\Microsoft.AspNet.Mvc.5.1.1\lib\net45\System.Web.Mvc.dll 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {459c6481-9884-497b-be64-06b6b832b073} 69 | Revalee.Client 70 | 71 | 72 | 73 | 74 | PreserveNewest 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /Revalee.Client.Mvc/RevaleeClientSettingsAttribute.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Web.Mvc; 31 | 32 | namespace Revalee.Client.Mvc 33 | { 34 | /// Represents an attribute that is used to configure callback requests made during this request. 35 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = false)] 36 | public sealed class RevaleeClientSettingsAttribute : FilterAttribute, IActionFilter 37 | { 38 | private readonly object _TypeId = new object(); 39 | private Uri _ServiceBaseUri; 40 | private int? _RequestTimeout; 41 | 42 | /// Gets or sets the service base URL used to make callback requests. 43 | /// The service base URL used to make callback requests. 44 | public string ServiceBaseUri 45 | { 46 | get 47 | { 48 | if (_ServiceBaseUri == null) 49 | { 50 | return null; 51 | } 52 | 53 | return _ServiceBaseUri.ToString(); 54 | } 55 | set 56 | { 57 | if (string.IsNullOrEmpty(value)) 58 | { 59 | _ServiceBaseUri = null; 60 | } 61 | else 62 | { 63 | _ServiceBaseUri = new ServiceBaseUri(value); 64 | } 65 | } 66 | } 67 | 68 | /// Gets or sets the timeout of callback requests in milliseconds, a value of 0 indicates a default timeout period, a value of -1 indicates an infinite timeout period. 69 | /// The timeout of callback requests in milliseconds, a value of 0 indicates a default timeout period, a value of -1 indicates an infinite timeout period. 70 | /// cannot be a negative value unless -1 for an infinite period. 71 | public int RequestTimeout 72 | { 73 | get 74 | { 75 | if (_RequestTimeout.HasValue) 76 | { 77 | return _RequestTimeout.Value; 78 | } 79 | else 80 | { 81 | return 0; 82 | } 83 | } 84 | set 85 | { 86 | if (value < -1) 87 | { 88 | throw new ArgumentOutOfRangeException("value"); 89 | } 90 | 91 | if (value == 0) 92 | { 93 | _RequestTimeout = null; 94 | } 95 | else 96 | { 97 | _RequestTimeout = value; 98 | } 99 | } 100 | } 101 | 102 | /// Gets the unique identifier for this attribute. 103 | /// The unique identifier for this attribute. 104 | public override object TypeId 105 | { 106 | get 107 | { 108 | return this._TypeId; 109 | } 110 | } 111 | 112 | /// Called by the ASP.NET MVC framework after the action method executes. 113 | /// The filter context. 114 | public void OnActionExecuted(ActionExecutedContext filterContext) 115 | { 116 | } 117 | 118 | /// Called by the ASP.NET MVC framework before the action method executes. 119 | /// The filter context. 120 | /// The parameter is null. 121 | public void OnActionExecuting(ActionExecutingContext filterContext) 122 | { 123 | if (filterContext == null) 124 | { 125 | throw new ArgumentNullException("filterContext"); 126 | } 127 | 128 | if (_RequestTimeout.HasValue) 129 | { 130 | RevaleeClientSettings.RequestTimeout = _RequestTimeout; 131 | } 132 | 133 | if (_ServiceBaseUri != null) 134 | { 135 | RevaleeClientSettings.ServiceBaseUri = _ServiceBaseUri; 136 | } 137 | } 138 | } 139 | } -------------------------------------------------------------------------------- /Revalee.Client.Mvc/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Revalee.Client/Configuration/ClientSettingsConfigurationElement.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Configuration; 31 | 32 | namespace Revalee.Client.Configuration 33 | { 34 | internal class ClientSettingsConfigurationElement : ConfigurationElement 35 | { 36 | [ConfigurationProperty("serviceBaseUri", IsKey = false, IsRequired = false)] 37 | [UrlValidator(AllowAbsolute = true, AllowRelative = false)] 38 | public Uri ServiceBaseUri 39 | { 40 | get 41 | { 42 | return (Uri)this["serviceBaseUri"]; 43 | } 44 | } 45 | 46 | [ConfigurationProperty("requestTimeout", DefaultValue = 0, IsKey = false, IsRequired = false)] 47 | [IntegerValidator(ExcludeRange = false, MinValue = -1)] 48 | public int RequestTimeout 49 | { 50 | get 51 | { 52 | return (int)this["requestTimeout"]; 53 | } 54 | } 55 | 56 | [ConfigurationProperty("authorizationKey", IsKey = false, IsRequired = false)] 57 | public string AuthorizationKey 58 | { 59 | get 60 | { 61 | return (string)this["authorizationKey"]; 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /Revalee.Client/Configuration/RevaleeSection.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using Revalee.Client.RecurringTasks; 30 | using System.Configuration; 31 | 32 | namespace Revalee.Client.Configuration 33 | { 34 | internal class RevaleeSection : ConfigurationSection 35 | { 36 | public static RevaleeSection GetConfiguration() 37 | { 38 | return (RevaleeSection)System.Configuration.ConfigurationManager.GetSection("revalee"); 39 | } 40 | 41 | [ConfigurationProperty("clientSettings", IsDefaultCollection = false, IsKey = false, IsRequired = false)] 42 | public ClientSettingsConfigurationElement ClientSettings 43 | { 44 | get { return (ClientSettingsConfigurationElement)this["clientSettings"]; } 45 | } 46 | 47 | [ConfigurationProperty("recurringTasks", IsDefaultCollection = true, IsKey = false, IsRequired = false)] 48 | [ConfigurationCollection(typeof(TaskConfigurationCollection), AddItemName = "task")] 49 | public TaskConfigurationCollection RecurringTasks 50 | { 51 | get { return (TaskConfigurationCollection)this["recurringTasks"]; } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /Revalee.Client/Configuration/UrlValidatorAttribute.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Configuration; 31 | 32 | namespace Revalee.Client.Configuration 33 | { 34 | internal sealed class UrlValidatorAttribute : ConfigurationValidatorAttribute 35 | { 36 | private bool _AllowAbsolute = true; 37 | 38 | private bool _AllowRelative = true; 39 | 40 | public UrlValidatorAttribute() 41 | { 42 | } 43 | 44 | public bool AllowAbsolute 45 | { 46 | get 47 | { 48 | return _AllowAbsolute; 49 | } 50 | set 51 | { 52 | _AllowAbsolute = value; 53 | } 54 | } 55 | 56 | public bool AllowRelative 57 | { 58 | get 59 | { 60 | return _AllowRelative; 61 | } 62 | set 63 | { 64 | _AllowRelative = value; 65 | } 66 | } 67 | 68 | public override ConfigurationValidatorBase ValidatorInstance 69 | { 70 | get 71 | { 72 | return new UrlValidator(_AllowAbsolute, _AllowRelative); 73 | } 74 | } 75 | 76 | private class UrlValidator : ConfigurationValidatorBase 77 | { 78 | private bool _AllowAbsolute = true; 79 | private bool _AllowRelative = true; 80 | 81 | public UrlValidator(bool allowAbsolute, bool allowRelative) 82 | { 83 | _AllowAbsolute = allowAbsolute; 84 | _AllowRelative = allowRelative; 85 | } 86 | 87 | public override bool CanValidate(Type type) 88 | { 89 | return type == typeof(Uri); 90 | } 91 | 92 | public override void Validate(object value) 93 | { 94 | if (value == null) 95 | { 96 | return; 97 | } 98 | 99 | if (value.GetType() != typeof(Uri)) 100 | { 101 | throw new ArgumentException("The URL attribute is invalid."); 102 | } 103 | 104 | Uri url = value as Uri; 105 | 106 | if (!_AllowAbsolute && url.IsAbsoluteUri) 107 | { 108 | throw new ArgumentException("The URL attribute cannot contain an absolute URL."); 109 | } 110 | 111 | if (!_AllowRelative && !url.IsAbsoluteUri) 112 | { 113 | throw new ArgumentException("The URL attribute cannot contain a relative URL."); 114 | } 115 | 116 | if (url.IsAbsoluteUri && url.Scheme != Uri.UriSchemeHttp && url.Scheme != Uri.UriSchemeHttps) 117 | { 118 | throw new ArgumentException(string.Format("The URL attribute only supports {0} and {1}.", Uri.UriSchemeHttp, Uri.UriSchemeHttps)); 119 | } 120 | } 121 | } 122 | } 123 | } -------------------------------------------------------------------------------- /Revalee.Client/License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sage Analytic Technologies, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Revalee.Client/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | 5 | [assembly: AssemblyTitle("Revalee.Client")] 6 | [assembly: AssemblyDescription("ASP.NET client library for scheduled web callbacks.")] 7 | [assembly: AssemblyConfiguration("")] 8 | [assembly: AssemblyCompany("Sage Analytic")] 9 | [assembly: AssemblyProduct("Revalee.Client")] 10 | [assembly: AssemblyCopyright("Copyright © 2014, Sage Analytic Technologies, LLC")] 11 | [assembly: AssemblyTrademark("")] 12 | [assembly: AssemblyCulture("")] 13 | [assembly: CLSCompliant(true)] 14 | [assembly: ComVisible(false)] 15 | [assembly: Guid("9386ce45-3c04-4ec5-a9c6-01ac6dc461f2")] 16 | [assembly: AssemblyVersion("2.0.0")] 17 | [assembly: AssemblyFileVersion("2.3.1")] 18 | [assembly: AssemblyInformationalVersion("2.3.1")] -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/ActivationFailureEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | /// 34 | /// Represents event arguments for a failure to activate the recurring task module. 35 | /// 36 | public sealed class ActivationFailureEventArgs : EventArgs 37 | { 38 | /// 39 | /// Creates an instance of the class. 40 | /// 41 | /// A value representing the number of consecutive failures to activate. 42 | public ActivationFailureEventArgs(int failureCount) 43 | { 44 | if (failureCount < 1) 45 | { 46 | throw new ArgumentOutOfRangeException("failureCount"); 47 | } 48 | 49 | this.FailureCount = failureCount; 50 | } 51 | 52 | /// 53 | /// Gets the number of consecutive failures. 54 | /// 55 | public int FailureCount 56 | { 57 | get; 58 | private set; 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/ActivationState.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System.Threading; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | internal class ActivationState 34 | { 35 | private int _Value; 36 | 37 | public bool IsActive 38 | { 39 | get 40 | { 41 | return _Value == 1; 42 | } 43 | set 44 | { 45 | _Value = Interlocked.Exchange(ref _Value, value ? 1 : 0); 46 | } 47 | } 48 | 49 | public ActivationState(bool initialValue = false) 50 | { 51 | _Value = initialValue ? 1 : 0; 52 | } 53 | 54 | public bool TransitionToActive() 55 | { 56 | return Interlocked.CompareExchange(ref _Value, 1, 0) == 0; 57 | } 58 | 59 | public bool TransitionToInactive() 60 | { 61 | return Interlocked.CompareExchange(ref _Value, 0, 1) == 1; 62 | } 63 | 64 | public static implicit operator bool(ActivationState state) 65 | { 66 | return state._Value == 1; 67 | } 68 | 69 | public static implicit operator ActivationState(bool isActive) 70 | { 71 | return new ActivationState(isActive); 72 | } 73 | 74 | public override bool Equals(object obj) 75 | { 76 | if (obj is bool) 77 | { 78 | return this.Equals((bool)obj); 79 | } 80 | 81 | return base.Equals(obj); 82 | } 83 | 84 | public override int GetHashCode() 85 | { 86 | return base.GetHashCode(); 87 | } 88 | 89 | public bool Equals(bool isActive) 90 | { 91 | return (isActive ? 1 : 0) == _Value; 92 | } 93 | 94 | public static bool operator ==(ActivationState a, bool b) 95 | { 96 | return (a._Value == 1) == b; 97 | } 98 | 99 | public static bool operator !=(ActivationState a, bool b) 100 | { 101 | return (a._Value == 1) != b; 102 | } 103 | 104 | public static bool operator ==(bool a, ActivationState b) 105 | { 106 | return a == (b._Value == 1); 107 | } 108 | 109 | public static bool operator !=(bool a, ActivationState b) 110 | { 111 | return a != (b._Value == 1); 112 | } 113 | } 114 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/CallbackRequest.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | internal class CallbackRequest 34 | { 35 | internal CallbackRequest(DateTimeOffset callbackTime, Uri callbackUri) 36 | { 37 | this.CallbackTime = callbackTime; 38 | this.CallbackUri = callbackUri; 39 | } 40 | 41 | internal DateTimeOffset CallbackTime 42 | { 43 | get; 44 | private set; 45 | } 46 | 47 | internal Uri CallbackUri 48 | { 49 | get; 50 | private set; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/ConfiguredTask.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Threading; 31 | 32 | namespace Revalee.Client.RecurringTasks 33 | { 34 | internal class ConfiguredTask : IRecurringTask 35 | { 36 | private IClockSource _ClockSource; 37 | private long _LastOccurrence; 38 | 39 | internal ConfiguredTask(string identifier, IClockSource clockSource, PeriodicityType periodicity, int hourOffset, int minuteOffset, Uri url) 40 | { 41 | this.Identifier = identifier; 42 | this._ClockSource = clockSource; 43 | this.Periodicity = periodicity; 44 | this.HourOffset = hourOffset; 45 | this.MinuteOffset = minuteOffset; 46 | this.Url = url; 47 | } 48 | 49 | internal bool HasOccurred 50 | { 51 | get 52 | { 53 | return (Interlocked.Read(ref _LastOccurrence) != 0L); 54 | } 55 | } 56 | 57 | internal long GetNextOccurrence() 58 | { 59 | DateTimeOffset now = _ClockSource.Now; 60 | DateTimeOffset next; 61 | 62 | switch (this.Periodicity) 63 | { 64 | case PeriodicityType.Hourly: 65 | next = new DateTimeOffset(now.Year, now.Month, now.Day, now.Hour, this.MinuteOffset, 0, now.Offset); 66 | 67 | if (next <= now) 68 | { 69 | next = next.AddHours(1.0); 70 | } 71 | 72 | break; 73 | 74 | case PeriodicityType.Daily: 75 | next = new DateTimeOffset(now.Year, now.Month, now.Day, this.HourOffset, this.MinuteOffset, 0, now.Offset); 76 | 77 | if (next <= now) 78 | { 79 | next = next.AddDays(1.0); 80 | } 81 | 82 | break; 83 | 84 | default: 85 | goto case PeriodicityType.Hourly; 86 | } 87 | 88 | return next.ToUniversalTime().Ticks; 89 | } 90 | 91 | internal bool SetLastOccurrence(long occurrence) 92 | { 93 | do 94 | { 95 | long lastOccurrence = Interlocked.Read(ref _LastOccurrence); 96 | 97 | if (lastOccurrence >= occurrence) 98 | { 99 | return false; 100 | } 101 | 102 | if (Interlocked.CompareExchange(ref _LastOccurrence, occurrence, lastOccurrence) == lastOccurrence) 103 | { 104 | return true; 105 | } 106 | } 107 | while (true); 108 | } 109 | 110 | public string Identifier 111 | { 112 | get; 113 | private set; 114 | } 115 | 116 | public PeriodicityType Periodicity 117 | { 118 | get; 119 | private set; 120 | } 121 | 122 | public int HourOffset 123 | { 124 | get; 125 | private set; 126 | } 127 | 128 | public int MinuteOffset 129 | { 130 | get; 131 | private set; 132 | } 133 | 134 | public Uri Url 135 | { 136 | get; 137 | private set; 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/DeactivationEventArgs.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | /// 34 | /// Represents event arguments for a deactivation of the recurring task module. 35 | /// 36 | public sealed class DeactivationEventArgs : EventArgs 37 | { 38 | /// 39 | /// Creates an instance of the class. 40 | /// 41 | /// A that is the cause of the deactivation. 42 | public DeactivationEventArgs(RevaleeRequestException exception) 43 | { 44 | this.Exception = exception; 45 | } 46 | 47 | /// 48 | /// Gets the that caused the deactivation. 49 | /// 50 | public RevaleeRequestException Exception 51 | { 52 | get; 53 | private set; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/EnumConfigurationConverter.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.ComponentModel; 31 | using System.Configuration; 32 | using System.Globalization; 33 | 34 | namespace Revalee.Client.RecurringTasks 35 | { 36 | internal sealed class EnumConfigurationConverter : ConfigurationConverterBase 37 | { 38 | public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) 39 | { 40 | return Enum.Parse(typeof(T), (string)value, true); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/IClockSource.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | /// 34 | /// Represents a time source used to schedule recurring tasks. 35 | /// 36 | public interface IClockSource 37 | { 38 | /// 39 | /// Gets the current to be used for scheduling callbacks. 40 | /// 41 | DateTimeOffset Now { get; } 42 | } 43 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/IRecurringTask.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | /// 34 | /// Represents a recurring task. 35 | /// 36 | public interface IRecurringTask 37 | { 38 | /// 39 | /// Gets the unique identifier of the scheduled task. 40 | /// 41 | string Identifier { get; } 42 | 43 | /// 44 | /// Gets the of the scheduled task. 45 | /// 46 | PeriodicityType Periodicity { get; } 47 | 48 | /// 49 | /// Gets the value of the scheduled hour (0-23). 50 | /// 51 | int HourOffset { get; } 52 | 53 | /// 54 | /// Gets the value of the scheduled minute (0-59). 55 | /// /// 56 | int MinuteOffset { get; } 57 | 58 | /// 59 | /// Gets the defining the target of the callback. 60 | /// 61 | Uri Url { get; } 62 | } 63 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/ITaskCollection.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System.Collections.Generic; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | internal interface ITaskCollection 34 | { 35 | int Count { get; } 36 | 37 | IEnumerable Tasks { get; } 38 | 39 | bool TryGetTask(string identifier, out ConfiguredTask taskConfig); 40 | 41 | bool Add(ConfiguredTask taskConfig); 42 | 43 | bool Remove(string identifier); 44 | 45 | bool Remove(ConfiguredTask taskConfig); 46 | } 47 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/ITaskManifest.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Collections.Generic; 31 | 32 | namespace Revalee.Client.RecurringTasks 33 | { 34 | /// 35 | /// Represents a manifest with information on recurring tasks. 36 | /// 37 | public interface ITaskManifest 38 | { 39 | /// 40 | /// Gets a value indicating the activation state of the recurring task module. 41 | /// 42 | bool IsActive { get; } 43 | 44 | /// 45 | /// Gets a value indicating that there are no recurring tasks defined. 46 | /// 47 | bool IsEmpty { get; } 48 | 49 | /// 50 | /// Gets or sets the defining the base URL for callbacks. 51 | /// 52 | Uri CallbackBaseUri { get; set; } 53 | 54 | /// 55 | /// Gets the enumeration of defined objects. 56 | /// 57 | IEnumerable Tasks { get; } 58 | 59 | /// 60 | /// Creates a callback task with a daily recurrence. 61 | /// 62 | /// A value for the scheduled hour (0-23). 63 | /// A value for the scheduled minute (0-59). 64 | /// A value defining the target of the callback. 65 | /// is not between 0 and 23. 66 | /// is not between 0 and 59. 67 | /// is not an absolute URL. 68 | /// contains an unsupported URL scheme. 69 | void AddDailyTask(int hour, int minute, Uri url); 70 | 71 | /// 72 | /// Creates a callback task with a daily recurrence. 73 | /// 74 | /// A value for the scheduled minute (0-59). 75 | /// A value defining the target of the callback. 76 | /// is not between 0 and 59. 77 | /// is not an absolute URL. 78 | /// contains an unsupported URL scheme. 79 | void AddHourlyTask(int minute, Uri url); 80 | 81 | /// 82 | /// Removes a recurring callback task. 83 | /// 84 | /// The of the task to be removed. 85 | /// is null. 86 | void RemoveTask(string identifier); 87 | 88 | /// 89 | /// An event handler triggered when the recurring task module is activated. 90 | /// 91 | event EventHandler Activated; 92 | 93 | /// 94 | /// An event handler triggered when the recurring task module is deactivated. 95 | /// 96 | event EventHandler Deactivated; 97 | 98 | /// 99 | /// An event handler triggered when an attempt to activate fails. 100 | /// 101 | event EventHandler ActivationFailed; 102 | } 103 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/ImmutableTaskCollection.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Collections.Generic; 31 | using System.Threading; 32 | 33 | namespace Revalee.Client.RecurringTasks 34 | { 35 | internal class ImmutableTaskCollection : ITaskCollection 36 | { 37 | private Dictionary _Tasks = new Dictionary(); 38 | 39 | internal ImmutableTaskCollection() 40 | { 41 | } 42 | 43 | internal ImmutableTaskCollection(IEnumerable tasks) 44 | { 45 | foreach (ConfiguredTask taskConfig in tasks) 46 | { 47 | if (!_Tasks.ContainsKey(taskConfig.Identifier)) 48 | { 49 | _Tasks.Add(taskConfig.Identifier, taskConfig); 50 | } 51 | } 52 | } 53 | 54 | public int Count 55 | { 56 | get 57 | { 58 | return _Tasks.Count; 59 | } 60 | } 61 | 62 | public IEnumerable Tasks 63 | { 64 | get 65 | { 66 | return _Tasks.Values; 67 | } 68 | } 69 | 70 | public bool TryGetTask(string identifier, out ConfiguredTask taskConfig) 71 | { 72 | return _Tasks.TryGetValue(identifier, out taskConfig); 73 | } 74 | 75 | public bool Add(ConfiguredTask taskConfig) 76 | { 77 | // A proper immutable dictionary class would simplify this routine 78 | RandomWaitScheduler retryScheduler = null; 79 | 80 | do 81 | { 82 | Dictionary original = _Tasks; 83 | 84 | if (!original.ContainsKey(taskConfig.Identifier)) 85 | { 86 | var clone = new Dictionary(original); 87 | 88 | if (!clone.ContainsKey(taskConfig.Identifier)) 89 | { 90 | clone.Add(taskConfig.Identifier, taskConfig); 91 | 92 | if (!object.ReferenceEquals(Interlocked.CompareExchange(ref _Tasks, clone, original), original)) 93 | { 94 | if (retryScheduler == null) 95 | { 96 | retryScheduler = new RandomWaitScheduler(); 97 | } 98 | 99 | retryScheduler.Wait(); 100 | continue; 101 | } 102 | 103 | return true; 104 | } 105 | } 106 | 107 | return false; 108 | } while (true); 109 | } 110 | 111 | public bool Remove(string identifier) 112 | { 113 | // A proper immutable dictionary class would simplify this routine 114 | RandomWaitScheduler retryScheduler = null; 115 | 116 | do 117 | { 118 | Dictionary original = _Tasks; 119 | 120 | if (original.ContainsKey(identifier)) 121 | { 122 | var clone = new Dictionary(original); 123 | 124 | if (clone.ContainsKey(identifier)) 125 | { 126 | clone.Remove(identifier); 127 | 128 | if (!object.ReferenceEquals(Interlocked.CompareExchange(ref _Tasks, clone, original), original)) 129 | { 130 | if (retryScheduler == null) 131 | { 132 | retryScheduler = new RandomWaitScheduler(); 133 | } 134 | 135 | retryScheduler.Wait(); 136 | continue; 137 | } 138 | 139 | return true; 140 | } 141 | } 142 | 143 | return false; 144 | } while (true); 145 | } 146 | 147 | public bool Remove(ConfiguredTask taskConfig) 148 | { 149 | return Remove(taskConfig.Identifier); 150 | } 151 | 152 | private class RandomWaitScheduler 153 | { 154 | private readonly Random _RandomNumberGenerator = new Random(); 155 | 156 | public void Wait() 157 | { 158 | Thread.Sleep(_RandomNumberGenerator.Next(15)); 159 | } 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/PeriodicityType.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | namespace Revalee.Client.RecurringTasks 30 | { 31 | /// 32 | /// Represents the periodicity of a recurring task. 33 | /// 34 | public enum PeriodicityType 35 | { 36 | /// 37 | /// An hourly recurrence schedule. 38 | /// 39 | Hourly, 40 | 41 | /// 42 | /// A daily recurrence schedule. 43 | /// 44 | Daily 45 | } 46 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/RecurringTaskCallbackDetails.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | /// 34 | /// The details of a callback initiated by a recurring task. 35 | /// 36 | public class RecurringTaskCallbackDetails 37 | { 38 | private string _CallbackId; 39 | private string _CallbackTime; 40 | private string _CurrentServiceTime; 41 | 42 | internal RecurringTaskCallbackDetails(string callbackId, string callbackTime, string currentServiceTime) 43 | { 44 | _CallbackId = callbackId; 45 | _CallbackTime = callbackTime; 46 | _CurrentServiceTime = currentServiceTime; 47 | } 48 | 49 | /// 50 | /// Gets the identifying this callback. 51 | /// 52 | public Guid CallbackId 53 | { 54 | get 55 | { 56 | return Guid.Parse(_CallbackId); 57 | } 58 | } 59 | 60 | /// 61 | /// Gets the scheduled of this callback. 62 | /// 63 | public DateTimeOffset CallbackTime 64 | { 65 | get 66 | { 67 | return DateTimeOffset.Parse(_CallbackTime).ToLocalTime(); 68 | } 69 | } 70 | 71 | /// 72 | /// Gets the of the moment this callback was issued according to the Revalee service that issued the callback. 73 | /// 74 | public DateTimeOffset CurrentServiceTime 75 | { 76 | get 77 | { 78 | return DateTimeOffset.Parse(_CurrentServiceTime).ToLocalTime(); 79 | } 80 | } 81 | } 82 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/RequestAnalysis.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | namespace Revalee.Client.RecurringTasks 30 | { 31 | internal class RequestAnalysis 32 | { 33 | internal bool IsRecurringTask = false; 34 | internal string TaskIdentifier = string.Empty; 35 | internal long Occurrence = 0; 36 | } 37 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/SystemClock.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | 31 | namespace Revalee.Client.RecurringTasks 32 | { 33 | internal sealed class SystemClock : IClockSource 34 | { 35 | public static readonly IClockSource Instance = new SystemClock(); 36 | 37 | private SystemClock() 38 | { 39 | } 40 | 41 | public DateTimeOffset Now 42 | { 43 | get 44 | { 45 | return DateTimeOffset.Now; 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/TaskBuilder.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Globalization; 31 | using System.Security.Cryptography; 32 | using System.Text; 33 | 34 | namespace Revalee.Client.RecurringTasks 35 | { 36 | internal class TaskBuilder : IDisposable 37 | { 38 | private Uri _CallbackBaseUri; 39 | private HashAlgorithm _HashAlgorithm; 40 | 41 | internal TaskBuilder() 42 | : this(null) 43 | { 44 | } 45 | 46 | internal TaskBuilder(Uri callbackBaseUri) 47 | { 48 | _CallbackBaseUri = callbackBaseUri; 49 | _HashAlgorithm = SHA1Managed.Create(); 50 | } 51 | 52 | internal ConfiguredTask Create(IClockSource clockSource, PeriodicityType periodicity, int hourOffset, int minuteOffset, Uri url) 53 | { 54 | Uri absoluteUrl; 55 | 56 | if (url.IsAbsoluteUri) 57 | { 58 | absoluteUrl = url; 59 | } 60 | else 61 | { 62 | if (_CallbackBaseUri == null) 63 | { 64 | throw new InvalidOperationException(string.Format("The recurring task targeting \"{0}\" is not an absolute URL and no callbackBaseUri attribute was supplied.", url)); 65 | } 66 | 67 | if (!Uri.TryCreate(_CallbackBaseUri, url, out absoluteUrl)) 68 | { 69 | throw new InvalidOperationException(string.Format("The recurring task targeting \"{0}\" is not an absolute URL and it cannot be combined with the callbackBaseUri attribute of \"{1}\".", url, _CallbackBaseUri)); 70 | } 71 | } 72 | 73 | string identifier = this.CreateTaskIdentifier(periodicity, hourOffset, minuteOffset, absoluteUrl); 74 | return new ConfiguredTask(identifier, clockSource, periodicity, hourOffset, minuteOffset, absoluteUrl); 75 | } 76 | 77 | private string CreateTaskIdentifier(PeriodicityType periodicity, int hourOffset, int minuteOffset, Uri url) 78 | { 79 | string keyFormat; 80 | 81 | switch (periodicity) 82 | { 83 | case PeriodicityType.Hourly: 84 | keyFormat = "H~XX:{2:00}~{3}"; 85 | break; 86 | 87 | case PeriodicityType.Daily: 88 | keyFormat = "D~{1:00}:{2:00}~{3}"; 89 | break; 90 | 91 | default: 92 | keyFormat = "{0}~{1:00}:{2:00}~{3}"; 93 | break; 94 | } 95 | 96 | string compoundKey = string.Format(CultureInfo.InvariantCulture, keyFormat, (int)periodicity, hourOffset, minuteOffset, url); 97 | byte[] textBytes = Encoding.UTF8.GetBytes(compoundKey); 98 | byte[] hashBytes = _HashAlgorithm.ComputeHash(textBytes); 99 | return ConvertByteArrayToHexadecimalString(hashBytes); 100 | } 101 | 102 | private static string ConvertByteArrayToHexadecimalString(byte[] bytes) 103 | { 104 | char[] charArray = new char[bytes.Length * 2]; 105 | int byteValue; 106 | 107 | for (int index = 0; index < bytes.Length; index++) 108 | { 109 | byteValue = bytes[index] >> 4; 110 | charArray[index * 2] = (char)(55 + byteValue + (((byteValue - 10) >> 31) & -7)); 111 | byteValue = bytes[index] & 0xF; 112 | charArray[index * 2 + 1] = (char)(55 + byteValue + (((byteValue - 10) >> 31) & -7)); 113 | } 114 | 115 | return new string(charArray); 116 | } 117 | 118 | public void Dispose() 119 | { 120 | this.Dispose(true); 121 | } 122 | 123 | protected void Dispose(bool disposing) 124 | { 125 | if (disposing) 126 | { 127 | if (_HashAlgorithm != null) 128 | { 129 | _HashAlgorithm.Dispose(); 130 | _HashAlgorithm = null; 131 | } 132 | 133 | GC.SuppressFinalize(this); 134 | } 135 | } 136 | 137 | ~TaskBuilder() 138 | { 139 | this.Dispose(false); 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/TaskConfigurationCollection.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using Revalee.Client.Configuration; 30 | using System; 31 | using System.Configuration; 32 | 33 | namespace Revalee.Client.RecurringTasks 34 | { 35 | internal class TaskConfigurationCollection : ConfigurationElementCollection 36 | { 37 | public override ConfigurationElementCollectionType CollectionType 38 | { 39 | get 40 | { 41 | return ConfigurationElementCollectionType.BasicMap; 42 | } 43 | } 44 | 45 | public TaskConfigurationElement this[int Index] 46 | { 47 | get 48 | { 49 | return (TaskConfigurationElement)this.BaseGet(Index); 50 | } 51 | set 52 | { 53 | if (this.BaseGet(Index) != null) 54 | { 55 | this.BaseRemoveAt(Index); 56 | } 57 | 58 | this.BaseAdd(Index, value); 59 | } 60 | } 61 | 62 | protected override string ElementName 63 | { 64 | get { return "task"; } 65 | } 66 | 67 | protected override bool IsElementName(string elementName) 68 | { 69 | return (elementName == this.ElementName); 70 | } 71 | 72 | protected override ConfigurationElement CreateNewElement() 73 | { 74 | return new TaskConfigurationElement(); 75 | } 76 | 77 | protected override object GetElementKey(System.Configuration.ConfigurationElement element) 78 | { 79 | return ((TaskConfigurationElement)element).Key; 80 | } 81 | 82 | [ConfigurationProperty("callbackBaseUri", IsKey = false, IsRequired = false)] 83 | [UrlValidator(AllowAbsolute = true, AllowRelative = false)] 84 | public Uri CallbackBaseUri 85 | { 86 | get 87 | { 88 | return (Uri)this["callbackBaseUri"]; 89 | } 90 | } 91 | } 92 | } -------------------------------------------------------------------------------- /Revalee.Client/RecurringTasks/TaskConfigurationElement.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using Revalee.Client.Configuration; 30 | using System; 31 | using System.ComponentModel; 32 | using System.Configuration; 33 | 34 | namespace Revalee.Client.RecurringTasks 35 | { 36 | internal class TaskConfigurationElement : ConfigurationElement 37 | { 38 | public TaskConfigurationElement() 39 | { 40 | this.Key = Guid.NewGuid(); 41 | } 42 | 43 | internal Guid Key { get; private set; } 44 | 45 | [ConfigurationProperty("periodicity", IsKey = false, IsRequired = true)] 46 | [TypeConverter(typeof(EnumConfigurationConverter))] 47 | public PeriodicityType Periodicity 48 | { 49 | get 50 | { 51 | return (PeriodicityType)this["periodicity"]; 52 | } 53 | } 54 | 55 | [ConfigurationProperty("hour", DefaultValue = 0, IsKey = false, IsRequired = false)] 56 | [IntegerValidator(ExcludeRange = false, MinValue = 0, MaxValue = 23)] 57 | public int Hour 58 | { 59 | get 60 | { 61 | return (int)this["hour"]; 62 | } 63 | } 64 | 65 | [ConfigurationProperty("minute", IsKey = false, IsRequired = true)] 66 | [IntegerValidator(ExcludeRange = false, MinValue = 0, MaxValue = 59)] 67 | public int Minute 68 | { 69 | get 70 | { 71 | return (int)this["minute"]; 72 | } 73 | } 74 | 75 | [ConfigurationProperty("url", IsKey = false, IsRequired = true)] 76 | [UrlValidator(AllowAbsolute = true, AllowRelative = true)] 77 | public Uri Url 78 | { 79 | get 80 | { 81 | return (Uri)this["url"]; 82 | } 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /Revalee.Client/Revalee.Client.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {459C6481-9884-497B-BE64-06B6B832B073} 8 | Library 9 | Properties 10 | Revalee.Client 11 | Revalee.Client 12 | v4.0 13 | 512 14 | 15 | 16 | 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | none 27 | true 28 | bin\Release\ 29 | TRACE 30 | prompt 31 | 4 32 | bin\Release\Revalee.Client.xml 33 | 34 | 35 | true 36 | 37 | 38 | ..\Revalee.snk 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | PreserveNewest 82 | 83 | 84 | 85 | 92 | -------------------------------------------------------------------------------- /Revalee.Client/RevaleeRequestException.cs: -------------------------------------------------------------------------------- 1 | #region License 2 | 3 | /* 4 | The MIT License (MIT) 5 | 6 | Copyright (c) 2014 Sage Analytic Technologies, LLC 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy 9 | of this software and associated documentation files (the "Software"), to deal 10 | in the Software without restriction, including without limitation the rights 11 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | copies of the Software, and to permit persons to whom the Software is 13 | furnished to do so, subject to the following conditions: 14 | 15 | The above copyright notice and this permission notice shall be included in all 16 | copies or substantial portions of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 24 | SOFTWARE. 25 | */ 26 | 27 | #endregion License 28 | 29 | using System; 30 | using System.Runtime.Serialization; 31 | using System.Security.Permissions; 32 | 33 | namespace Revalee.Client 34 | { 35 | /// 36 | /// Represents errors that occur during the processing of a Revalee service request. 37 | /// 38 | [Serializable] 39 | public class RevaleeRequestException : Exception 40 | { 41 | /// 42 | /// Initializes a new instance of the class. 43 | /// 44 | /// A representing the scheme, host, and port for the Revalee service (example: http://localhost:46200). 45 | /// An absolute that will be requested on the callback. 46 | /// The exception that is the cause of the current exception. 47 | public RevaleeRequestException(Uri serviceBaseUri, Uri callbackUri, Exception innerException) 48 | : base("The request to the Revalee service was unsuccessful.", innerException) 49 | { 50 | this.ServiceBaseUri = serviceBaseUri; 51 | this.CallbackUri = callbackUri; 52 | } 53 | 54 | /// 55 | /// Initializes a new instance of the class with serialized data. 56 | /// 57 | /// The that holds the serialized object data about the exception being thrown. 58 | /// The that contains contextual information about the source or destination. 59 | /// is null. 60 | protected RevaleeRequestException(SerializationInfo info, StreamingContext context) 61 | : base(info, context) 62 | { 63 | if (info == null) 64 | { 65 | throw new ArgumentNullException("info"); 66 | } 67 | 68 | this.ServiceBaseUri = new Uri(info.GetString("ServiceBaseUri")); 69 | this.CallbackUri = new Uri(info.GetString("CallbackUri")); 70 | } 71 | 72 | /// Gets the service base Uri used to make this Revalee service request. 73 | /// The service base Uri used to make this Revalee service request. 74 | public Uri ServiceBaseUri { get; private set; } 75 | 76 | /// Gets the callback Uri used to make this Revalee service request. 77 | /// The callback Uri used to make this Revalee service request. 78 | public Uri CallbackUri { get; private set; } 79 | 80 | /// 81 | /// Sets the with information about the exception. 82 | /// 83 | /// The that holds the serialized object data about the exception being thrown. 84 | /// The that contains contextual information about the source or destination. 85 | /// is null. 86 | [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] 87 | public override void GetObjectData(SerializationInfo info, StreamingContext context) 88 | { 89 | if (info == null) 90 | { 91 | throw new ArgumentNullException("info"); 92 | } 93 | 94 | base.GetObjectData(info, context); 95 | 96 | info.AddValue("ServiceBaseUri", this.ServiceBaseUri.ToString()); 97 | info.AddValue("CallbackUri", this.CallbackUri.ToString()); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/App_Start/RouteConfig.cs: -------------------------------------------------------------------------------- 1 | using System.Web.Mvc; 2 | using System.Web.Routing; 3 | 4 | namespace Revalee.SampleSite 5 | { 6 | public class RouteConfig 7 | { 8 | public static void RegisterRoutes(RouteCollection routes) 9 | { 10 | routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 11 | 12 | routes.MapRoute( 13 | name: "Default", 14 | url: "{controller}/{action}/{id}", 15 | defaults: new { controller = "RevaleeTest", action = "Index", id = UrlParameter.Optional } 16 | ); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/Content/Site.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fff; 3 | color: #000; 4 | font-family: 'Segoe UI', sans-serif; 5 | font-size: 0.9em; 6 | padding: 10px; 7 | } 8 | 9 | h2 { 10 | margin-top: 0px; 11 | font-weight: normal; 12 | line-height: 1.0em; 13 | } 14 | 15 | h4 { 16 | font-weight: normal; 17 | line-height: 1.0em; 18 | color: #666; 19 | } 20 | 21 | input { 22 | font-family: 'Segoe UI', sans-serif; 23 | font-size: 0.9em; 24 | margin: 3px; 25 | border: 1px solid #aaa; 26 | background-color: #f6f6f6; 27 | } 28 | 29 | fieldset { 30 | border: 1px solid #ccc; 31 | margin: 0px; 32 | padding: 10px; 33 | } 34 | 35 | label { 36 | color: #666; 37 | padding-right: 4px; 38 | } 39 | 40 | .labeled-input { 41 | display: inline-block; 42 | margin-right: 10px; 43 | white-space: nowrap; 44 | } 45 | 46 | #serviceBaseUrl { 47 | width: 230px; 48 | } 49 | 50 | #callbackTime { 51 | width: 210px; 52 | } 53 | 54 | #parameterValue { 55 | width: 160px; 56 | } 57 | 58 | #asyncOption { 59 | margin-right: 4px; 60 | vertical-align: middle; 61 | } 62 | 63 | button { 64 | font-family: 'Segoe UI', sans-serif; 65 | font-size: 0.9em; 66 | } 67 | 68 | #results { 69 | margin: 20px 0px; 70 | min-height: 100px; 71 | } 72 | 73 | table { 74 | border: 1px solid #ccc; 75 | width: 100%; 76 | -moz-box-sizing: padding-box; 77 | box-sizing: padding-box; 78 | } 79 | 80 | tbody tr:nth-child(even) { 81 | background-color: #f6f6f6; 82 | } 83 | 84 | th { 85 | color: #666; 86 | font-weight: normal; 87 | padding: 2px 8px; 88 | border-bottom: 1px dotted #999; 89 | text-align: left; 90 | } 91 | 92 | td { 93 | padding: 8px; 94 | white-space: nowrap; 95 | } 96 | 97 | tfoot td { 98 | color: #000; 99 | font-weight: normal; 100 | padding: 2px 8px; 101 | border-top: 1px dotted #999; 102 | text-align: left; 103 | } 104 | 105 | tfoot td.right { 106 | text-align: right; 107 | } 108 | 109 | tfoot td label { 110 | color: #666; 111 | } 112 | 113 | footer { 114 | color: #666; 115 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/Controllers/RevaleeTestController.cs: -------------------------------------------------------------------------------- 1 | using Revalee.Client; 2 | using Revalee.Client.Mvc; 3 | using Revalee.Client.RecurringTasks; 4 | using Revalee.SampleSite.Infrastructure; 5 | using System; 6 | using System.Diagnostics; 7 | using System.Net; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using System.Web.Mvc; 11 | 12 | namespace Revalee.SampleSite.Controllers 13 | { 14 | [RevaleeClientSettings(RequestTimeout = 3000)] 15 | public class RevaleeTestController : Controller 16 | { 17 | private static CallbackLog _Log = new CallbackLog(); 18 | 19 | private static int _TotalRequestCount = 0; 20 | 21 | private static int _TotalCallbackCount = 0; 22 | 23 | public ActionResult Index() 24 | { 25 | return View(); 26 | } 27 | 28 | public ActionResult ViewLog() 29 | { 30 | ViewBag.TotalRequestCount = _TotalRequestCount; 31 | 32 | ViewBag.TotalCallbackCount = _TotalCallbackCount; 33 | 34 | return this.PartialView(_Log.Snapshot()); 35 | } 36 | 37 | public ActionResult Schedule(Uri serviceBaseUri, DateTimeOffset callbackTime, Uri callbackUri) 38 | { 39 | DateTimeOffset now = DateTimeOffset.Now; 40 | 41 | Interlocked.Increment(ref _TotalRequestCount); 42 | 43 | Guid callbackId = RevaleeRegistrar.ScheduleCallback(serviceBaseUri, callbackTime, callbackUri); 44 | 45 | _Log.Add(callbackId, callbackTime, callbackUri, now); 46 | 47 | return new HttpStatusCodeResult(HttpStatusCode.OK); 48 | } 49 | 50 | [RevaleeClientSettings(ServiceBaseUri = "http://localhost:46200", RequestTimeout = 3000)] // This attribute is only needed if your need to override the web.config settings 51 | public async Task ScheduleAsync(Uri serviceBaseUri, DateTimeOffset callbackTime, Uri callbackUri, CancellationToken cancellationToken) 52 | { 53 | DateTimeOffset now = DateTimeOffset.Now; 54 | 55 | Interlocked.Increment(ref _TotalRequestCount); 56 | 57 | // Supersede all configured values for ServiceBaseUri because the sample web page specifies this value at runtime. 58 | // Normally, this value would be configured in the web.config or by the RevaleeClientSettings attribute. 59 | RevaleeClientSettings.ServiceBaseUri = serviceBaseUri; 60 | 61 | Guid callbackId = await this.CallbackAtAsync(callbackUri, callbackTime, cancellationToken); 62 | 63 | _Log.Add(callbackId, callbackTime, callbackUri, now); 64 | 65 | return new HttpStatusCodeResult(HttpStatusCode.OK); 66 | } 67 | 68 | [AllowAnonymous] 69 | [HttpPost] 70 | [CallbackAction] // This attribute ensures that only legitimately requested callbacks get handled by this action 71 | public ActionResult Callback(Guid callbackId, DateTimeOffset callbackTime, DateTimeOffset currentServiceTime, string id) 72 | { 73 | DateTimeOffset now = DateTimeOffset.Now; 74 | 75 | if (_Log.Update(callbackId, currentServiceTime, this.Request.Url, id, now)) 76 | { 77 | Interlocked.Increment(ref _TotalCallbackCount); 78 | } 79 | 80 | return new HttpStatusCodeResult(HttpStatusCode.OK); 81 | } 82 | 83 | [AllowAnonymous] 84 | [HttpPost] 85 | [CallbackAction] // This attribute ensures that only legitimately requested callbacks get handled by this action 86 | public ActionResult ImmediateCallback(object state) 87 | { 88 | if (state == null) 89 | { 90 | Debug.WriteLine("Immediate callback triggered at {0:F} without a state object.", DateTime.Now); 91 | } 92 | else 93 | { 94 | Debug.WriteLine("Immediate callback triggered at {0:F} with a state object of: {1}.", DateTime.Now, state.ToString()); 95 | } 96 | 97 | return new HttpStatusCodeResult(HttpStatusCode.OK); 98 | } 99 | 100 | [HttpPost] 101 | public ActionResult Recurring() 102 | { 103 | if (RecurringTaskModule.IsProcessingRecurringCallback) 104 | { 105 | RecurringTaskCallbackDetails details = RecurringTaskModule.CallbackDetails; 106 | 107 | Debug.WriteLine("Recurring event triggered at {0:F}, requested at {1:F} by {2}.", DateTime.Now, details.CallbackTime.LocalDateTime, details.CallbackId); 108 | } 109 | 110 | return new HttpStatusCodeResult(HttpStatusCode.OK); 111 | } 112 | 113 | protected override void OnException(ExceptionContext filterContext) 114 | { 115 | if (filterContext != null && filterContext.Exception != null) 116 | { 117 | Debug.WriteLine("Error: " + filterContext.Exception.Message); 118 | } 119 | 120 | filterContext.ExceptionHandled = true; 121 | base.OnException(filterContext); 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/Global.asax: -------------------------------------------------------------------------------- 1 | <%@ Application Codebehind="Global.asax.cs" Inherits="Revalee.SampleSite.MvcApplication" Language="C#" %> 2 | -------------------------------------------------------------------------------- /Revalee.SampleSite/Global.asax.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Web.Mvc; 3 | using System.Web.Routing; 4 | 5 | namespace Revalee.SampleSite 6 | { 7 | public class MvcApplication : System.Web.HttpApplication 8 | { 9 | protected void Application_Start() 10 | { 11 | AreaRegistration.RegisterAllAreas(); 12 | RouteConfig.RegisterRoutes(RouteTable.Routes); 13 | 14 | var manifest = Revalee.Client.RecurringTasks.RecurringTaskModule.GetManifest(); 15 | 16 | manifest.Activated += RecurringTasks_Activated; 17 | manifest.Deactivated += RecurringTasks_Deactivated; 18 | manifest.ActivationFailed += RecurringTasks_ActivationFailed; 19 | } 20 | 21 | protected void Application_Error(object sender, EventArgs e) 22 | { 23 | Exception error = Server.GetLastError(); 24 | if (error != null) 25 | { 26 | System.Diagnostics.Debug.WriteLine("Error: " + error.Message); 27 | } 28 | } 29 | 30 | protected void RecurringTasks_Activated(object sender, EventArgs e) 31 | { 32 | System.Diagnostics.Debug.WriteLine("ACTIVATED"); 33 | } 34 | 35 | protected void RecurringTasks_Deactivated(object sender, Revalee.Client.RecurringTasks.DeactivationEventArgs e) 36 | { 37 | System.Diagnostics.Debug.WriteLine("DEACTIVATED: " + e.Exception.Message); 38 | } 39 | 40 | protected void RecurringTasks_ActivationFailed(object sender, Revalee.Client.RecurringTasks.ActivationFailureEventArgs e) 41 | { 42 | System.Diagnostics.Debug.WriteLine("ACTIVATION FAILURE: attempt #" + e.FailureCount.ToString()); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/Infrastructure/CallbackDetails.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Revalee.SampleSite.Infrastructure 4 | { 5 | public class CallbackDetails 6 | { 7 | public Guid CallbackId; 8 | public Uri CallbackUri; 9 | public DateTimeOffset ScheduledCallbackTime; 10 | public DateTimeOffset ClientRequestedTime; 11 | public DateTimeOffset? ServiceProcessedTime; 12 | public DateTimeOffset? ClientReceivedTime; 13 | public string ParameterValue; 14 | } 15 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/Infrastructure/CallbackLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | 5 | namespace Revalee.SampleSite.Infrastructure 6 | { 7 | public class CallbackLog 8 | { 9 | private Dictionary _Log = new Dictionary(); 10 | 11 | internal void Add(Guid callbackId, DateTimeOffset callbackTime, Uri callbackUri, DateTimeOffset clientRequestedTime) 12 | { 13 | var details = new CallbackDetails() { CallbackId = callbackId, ScheduledCallbackTime = callbackTime, CallbackUri = callbackUri, ClientRequestedTime = clientRequestedTime }; 14 | 15 | lock (_Log) 16 | { 17 | _Log.Add(callbackId, details); 18 | } 19 | } 20 | 21 | internal bool Update(Guid callbackId, DateTimeOffset currentServiceTime, Uri calledbackUri, string parameterValue, DateTimeOffset clientReceivedTime) 22 | { 23 | lock (_Log) 24 | { 25 | CallbackDetails details; 26 | 27 | if (_Log.TryGetValue(callbackId, out details)) 28 | { 29 | Debug.Assert(details.CallbackUri.OriginalString.Equals(calledbackUri.OriginalString)); 30 | details.ServiceProcessedTime = currentServiceTime; 31 | details.ClientReceivedTime = clientReceivedTime; 32 | details.ParameterValue = parameterValue; 33 | return true; 34 | } 35 | 36 | return false; 37 | } 38 | } 39 | 40 | internal List Snapshot() 41 | { 42 | lock (_Log) 43 | { 44 | return new List(_Log.Values); 45 | } 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Revalee.SampleSite/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("Revalee.SampleSite")] 8 | [assembly: AssemblyDescription("Website to demonstrate the use of Revalee.")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("Sage Analytic")] 11 | [assembly: AssemblyProduct("Revalee.SampleSite")] 12 | [assembly: AssemblyCopyright("Copyright © 2014, Sage Analytic Technologies, LLC")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("182effca-436a-45bd-9496-478a76d9ffdc")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Revision and Build Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("2.0.0")] 35 | [assembly: AssemblyFileVersion("2.3.1")] 36 | [assembly: AssemblyInformationalVersion("2.3.1")] -------------------------------------------------------------------------------- /Revalee.SampleSite/Views/RevaleeTest/Index.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | Revalee Sample Site 7 | 8 | 9 | 10 | 11 |

Revalee Sample Site

12 |

Testing console for the Revalee service.

13 |
14 |
15 |
16 |
17 |
18 |
19 | 20 |
21 |
22 |
23 |
24 |

© 2014 Sage Analytic Technologies, LLC. Client library: Revalee.Client.Mvc v@System.Diagnostics.FileVersionInfo.GetVersionInfo(System.Reflection.Assembly.GetAssembly(typeof(Revalee.Client.Mvc.RevaleeClientSettingsAttribute)).Location).ProductVersion

25 |
26 | 112 | 113 | -------------------------------------------------------------------------------- /Revalee.SampleSite/Views/RevaleeTest/ViewLog.cshtml: -------------------------------------------------------------------------------- 1 | @model IEnumerable 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | @foreach (var item in Model) 15 | { 16 | 17 | 20 | 23 | 26 | 29 | 32 | 35 | 36 | } 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
Requested AtCallback IDScheduled AtParameter ValueProcess DelayRoundtrip Time
18 | @item.ClientRequestedTime.ToLocalTime().ToString(@"yyyy-MM-dd\THH:mm:ss.fff") 19 | 21 | @item.CallbackId 22 | 24 | @item.ScheduledCallbackTime.ToLocalTime().ToString(@"yyyy-MM-dd\THH:mm:ss.FFF") 25 | 27 | @item.ParameterValue 28 | 30 | @(item.ServiceProcessedTime.HasValue ? item.ServiceProcessedTime.Value.Subtract(item.ScheduledCallbackTime).ToString(@"d\.hh\:mm\:ss\.fff") : "") 31 | 33 | @(item.ClientReceivedTime.HasValue ? item.ClientReceivedTime.Value.Subtract(item.ClientRequestedTime).ToString(@"d\.hh\:mm\:ss\.fff") : "") 34 |
@ViewBag.TotalRequestCount.ToString("#,##0") @ViewBag.TotalCallbackCount.ToString("#,##0")
-------------------------------------------------------------------------------- /Revalee.SampleSite/Views/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Revalee.SampleSite/Web.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Revalee.SampleSite/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/Revalee.SampleSite/favicon.ico -------------------------------------------------------------------------------- /Revalee.SampleSite/packages.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Revalee.Service/AbortableThreadPool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace Revalee.Service 6 | { 7 | internal static class AbortableThreadPool 8 | { 9 | private readonly static HashSet _WorkItems = new HashSet(); 10 | 11 | private sealed class WorkItem 12 | { 13 | public WaitCallback Callback; 14 | public object State; 15 | public ExecutionContext Context; 16 | public Thread Thread; 17 | } 18 | 19 | public static bool QueueUserWorkItem(WaitCallback callback, object state) 20 | { 21 | if (callback == null) 22 | { 23 | throw new ArgumentNullException("callback"); 24 | } 25 | 26 | WorkItem workItem = new WorkItem() 27 | { 28 | Callback = callback, 29 | State = state, 30 | Context = ExecutionContext.Capture() 31 | }; 32 | 33 | bool success = false; 34 | 35 | // Start tracking this work item 36 | lock (_WorkItems) 37 | { 38 | _WorkItems.Add(workItem); 39 | } 40 | 41 | try 42 | { 43 | // Place work item on the thread pool queue 44 | success = ThreadPool.QueueUserWorkItem(new WaitCallback(HandleCallback), workItem); 45 | } 46 | catch 47 | { 48 | // Work item could not be queued 49 | success = false; 50 | throw; 51 | } 52 | finally 53 | { 54 | if (!success) 55 | { 56 | // Stop tracking this work item 57 | RemoveWorkItem(workItem); 58 | } 59 | } 60 | 61 | return success; 62 | } 63 | 64 | public static void ForceClose() 65 | { 66 | lock (_WorkItems) 67 | { 68 | foreach (WorkItem workItem in _WorkItems) 69 | { 70 | // Check if the work item is currently executing 71 | if (workItem.Thread != null) 72 | { 73 | if (workItem.State == null) 74 | { 75 | workItem.Thread.Abort(); 76 | } 77 | else 78 | { 79 | workItem.Thread.Abort(workItem.State); 80 | } 81 | 82 | // Remove the ability to abort the work item thread 83 | workItem.Thread = null; 84 | } 85 | 86 | // Remove the ability to execute the work item delegate 87 | workItem.Callback = null; 88 | } 89 | 90 | // Clear all active work items 91 | _WorkItems.Clear(); 92 | } 93 | } 94 | 95 | private static void HandleCallback(object state) 96 | { 97 | WorkItem workItem = (WorkItem)state; 98 | 99 | lock (_WorkItems) 100 | { 101 | // Check if work item has been canceled 102 | if (workItem.Callback == null) 103 | { 104 | return; 105 | } 106 | 107 | // Track the thread assigned to this work item 108 | workItem.Thread = Thread.CurrentThread; 109 | } 110 | 111 | try 112 | { 113 | // Begin execution of the work item 114 | ExecutionContext.Run(workItem.Context, new ContextCallback(workItem.Callback.Invoke), workItem.State); 115 | } 116 | finally 117 | { 118 | // Stop tracking this work item 119 | RemoveWorkItem(workItem); 120 | } 121 | } 122 | 123 | private static void RemoveWorkItem(WorkItem workItem) 124 | { 125 | lock (_WorkItems) 126 | { 127 | // Remove the ability to execute the work item delegate 128 | workItem.Callback = null; 129 | 130 | // Remove the ability to abort the work item thread 131 | workItem.Thread = null; 132 | 133 | // Remove the work item from the list of active work items 134 | _WorkItems.Remove(workItem); 135 | } 136 | } 137 | } 138 | } -------------------------------------------------------------------------------- /Revalee.Service/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /Revalee.Service/ApplicationFolderHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Security.AccessControl; 5 | using System.Security.Principal; 6 | 7 | namespace Revalee.Service 8 | { 9 | internal static class ApplicationFolderHelper 10 | { 11 | private const string _CommonOrganizationFolderName = "Revalee"; 12 | private static string _ApplicationFolderName; 13 | private static object _SyncLock = new Object(); 14 | 15 | public static string ApplicationFolderName 16 | { 17 | get 18 | { 19 | if (_ApplicationFolderName == null) 20 | { 21 | lock (_SyncLock) 22 | { 23 | if (_ApplicationFolderName == null) 24 | { 25 | _ApplicationFolderName = EnsureApplicationFolder(); 26 | } 27 | } 28 | } 29 | 30 | return _ApplicationFolderName; 31 | } 32 | } 33 | 34 | private static string EnsureApplicationFolder() 35 | { 36 | string commonAppPath = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData); 37 | 38 | if (!Directory.Exists(commonAppPath)) 39 | { 40 | return Path.GetDirectoryName(Assembly.GetEntryAssembly().Location); 41 | } 42 | 43 | string applicationName = Assembly.GetEntryAssembly().GetName().Name; 44 | string organizationAppPath = Path.Combine(commonAppPath, _CommonOrganizationFolderName); 45 | 46 | if (!Directory.Exists(organizationAppPath)) 47 | { 48 | DirectoryInfo orgAppPathInfo = Directory.CreateDirectory(organizationAppPath); 49 | SetSecurityRights(orgAppPathInfo); 50 | } 51 | 52 | string applicationAppPath = Path.Combine(organizationAppPath, applicationName); 53 | 54 | if (!Directory.Exists(applicationAppPath)) 55 | { 56 | Directory.CreateDirectory(applicationAppPath); 57 | } 58 | 59 | return applicationAppPath; 60 | } 61 | 62 | private static void SetSecurityRights(DirectoryInfo directoryInfo) 63 | { 64 | bool modified; 65 | DirectorySecurity directorySecurity = directoryInfo.GetAccessControl(); 66 | SecurityIdentifier securityIdentifier = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); 67 | AccessRule rule = new FileSystemAccessRule( 68 | securityIdentifier, 69 | FileSystemRights.Write | 70 | FileSystemRights.ReadAndExecute | 71 | FileSystemRights.Modify, 72 | AccessControlType.Allow); 73 | directorySecurity.ModifyAccessRule(AccessControlModification.Add, rule, out modified); 74 | directoryInfo.SetAccessControl(directorySecurity); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /Revalee.Service/AuthorizationHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | 7 | namespace Revalee.Service 8 | { 9 | internal static class AuthorizationHelper 10 | { 11 | private delegate string CipherProcessor(IDictionary incomingCipherValues, Guid callbackId); 12 | 13 | internal static string ConstructResponse(string authorizationCipher, Guid callbackId) 14 | { 15 | if (string.IsNullOrEmpty(authorizationCipher)) 16 | { 17 | return null; 18 | } 19 | 20 | if (Guid.Empty.Equals(callbackId)) 21 | { 22 | return null; 23 | } 24 | 25 | IDictionary incomingCipherValues = ParseMultiValueHeader(authorizationCipher); 26 | 27 | short version = 0; 28 | if (!short.TryParse(incomingCipherValues["v"], out version) || version < 1) 29 | { 30 | return null; 31 | } 32 | 33 | CipherProcessor processor = AssignCipherProcessor(version); 34 | 35 | if (processor == null) 36 | { 37 | return null; 38 | } 39 | 40 | return processor(incomingCipherValues, callbackId); 41 | } 42 | 43 | private static string CipherProcessorVersion2(IDictionary incomingCipherValues, Guid callbackId) 44 | { 45 | // Create nonce byte array 46 | string nonceInHex = incomingCipherValues["n"]; 47 | byte[] nonce = ConvertHexToByteArray(nonceInHex); 48 | 49 | if (nonce == null) 50 | { 51 | return null; 52 | } 53 | 54 | // Create client cryptogram byte array 55 | string clientCryptogramInHex = incomingCipherValues["c"]; 56 | byte[] clientCryptogram = ConvertHexToByteArray(clientCryptogramInHex); 57 | 58 | if (clientCryptogram == null) 59 | { 60 | return null; 61 | } 62 | 63 | // Construct server cryptogram 64 | byte[] responseId = GetResponseId(callbackId); 65 | byte[] serverCryptogram = BuildServerCryptogram(nonce, clientCryptogram, responseId); 66 | 67 | // Build cipher string 68 | var outgoingCipher = new StringBuilder(255); 69 | 70 | outgoingCipher.Append("v="); 71 | outgoingCipher.Append(incomingCipherValues["v"]); 72 | 73 | outgoingCipher.Append(",n="); 74 | outgoingCipher.Append(incomingCipherValues["n"]); 75 | 76 | outgoingCipher.Append(",s="); 77 | 78 | for (int byteIndex = 0; byteIndex < serverCryptogram.Length; byteIndex++) 79 | { 80 | outgoingCipher.Append(serverCryptogram[byteIndex].ToString("X2", CultureInfo.InvariantCulture)); 81 | } 82 | 83 | return outgoingCipher.ToString(); 84 | } 85 | 86 | private static CipherProcessor AssignCipherProcessor(short version) 87 | { 88 | if (version == 2) 89 | { 90 | return CipherProcessorVersion2; 91 | } 92 | 93 | return null; 94 | } 95 | 96 | private static readonly byte[] _HexNibbles = new byte[] { 97 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 98 | 0x8, 0x9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 99 | 0xff, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xff, 100 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 101 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 102 | 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 103 | 0xff, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf}; 104 | 105 | private static byte[] ConvertHexToByteArray(string value) 106 | { 107 | if (value == null || value.Length == 0 || (value.Length & 1) != 0) 108 | { 109 | return null; 110 | } 111 | 112 | byte[] bytes = new byte[value.Length >> 1]; 113 | 114 | for (int i = 0; i < bytes.Length; i++) 115 | { 116 | int highNibbleOffset = value[i << 1] - 48; 117 | int lowNibbleOffset = value[(i << 1) + 1] - 48; 118 | 119 | if (highNibbleOffset < 0 || highNibbleOffset > 54 || lowNibbleOffset < 0 || lowNibbleOffset > 54) 120 | { 121 | return null; 122 | } 123 | 124 | byte highNibble = _HexNibbles[highNibbleOffset]; 125 | byte lowNibble = _HexNibbles[lowNibbleOffset]; 126 | 127 | if (highNibble == 0xff || lowNibble == 0xff) 128 | { 129 | return null; 130 | } 131 | 132 | bytes[i] = (byte)((highNibble << 4) | lowNibble); 133 | } 134 | 135 | return bytes; 136 | } 137 | 138 | private static byte[] GetResponseId(Guid callbackId) 139 | { 140 | return callbackId.ToByteArray(); 141 | } 142 | 143 | private static byte[] BuildServerCryptogram(byte[] nonce, byte[] clientCryptogram, byte[] responseId) 144 | { 145 | byte[] contents = new byte[nonce.Length + clientCryptogram.Length + responseId.Length]; 146 | 147 | Array.Copy(nonce, 0, contents, 0, nonce.Length); 148 | Array.Copy(clientCryptogram, 0, contents, nonce.Length, clientCryptogram.Length); 149 | Array.Copy(responseId, 0, contents, nonce.Length + clientCryptogram.Length, responseId.Length); 150 | 151 | using (var hashingAlgorithm = new SHA256Managed()) 152 | { 153 | return hashingAlgorithm.ComputeHash(contents); 154 | } 155 | } 156 | 157 | private static IDictionary ParseMultiValueHeader(string headerValue) 158 | { 159 | var dictionary = new Dictionary(10); 160 | 161 | string[] parts = headerValue.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); 162 | 163 | for (int partIndex = 0; partIndex < parts.Length; partIndex++) 164 | { 165 | string part = parts[partIndex]; 166 | 167 | int equalIndex = part.IndexOf('=', 0); 168 | 169 | if (equalIndex > 0 && equalIndex < part.Length - 1) 170 | { 171 | dictionary.Add(part.Substring(0, equalIndex).Trim(), part.Substring(equalIndex + 1).Trim()); 172 | } 173 | } 174 | 175 | return dictionary; 176 | } 177 | } 178 | } -------------------------------------------------------------------------------- /Revalee.Service/CommandRouter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal class CommandRouter 7 | { 8 | private readonly Dictionary _HandlerDirectory = new Dictionary(StringComparer.OrdinalIgnoreCase); 9 | 10 | internal class RequestHandlerInfo 11 | { 12 | public Type RequestHandlerType; 13 | public IRequestHandler ReentrantInstance; 14 | } 15 | 16 | public CommandRouter(RequestHandlerDirectory requestHandlers) 17 | { 18 | if (requestHandlers == null) 19 | { 20 | throw new ArgumentNullException("requestHandlers"); 21 | } 22 | 23 | if (requestHandlers.Count == 0) 24 | { 25 | throw new ArgumentException("No request handlers were found in the directory.", "requestHandlers"); 26 | } 27 | 28 | foreach (RequestHandlerMapping mapping in requestHandlers) 29 | { 30 | var handlerInfo = new RequestHandlerInfo() { RequestHandlerType = mapping.RequestHandlerType, ReentrantInstance = null }; 31 | 32 | try 33 | { 34 | _HandlerDirectory.Add(mapping.Command, handlerInfo); 35 | } 36 | catch (ArgumentException) 37 | { 38 | throw new ArgumentException("The same command was mapped to more than one request handler.", "requestHandlers"); 39 | } 40 | } 41 | } 42 | 43 | public IRequestHandler MapHandler(string url) 44 | { 45 | RequestHandlerInfo handlerInfo = null; 46 | 47 | if (!_HandlerDirectory.TryGetValue(ParseCommandFromUrl(url), out handlerInfo)) 48 | { 49 | return null; 50 | } 51 | 52 | if (handlerInfo == null) 53 | { 54 | return null; 55 | } 56 | 57 | if (handlerInfo.ReentrantInstance != null) 58 | { 59 | return handlerInfo.ReentrantInstance; 60 | } 61 | 62 | IRequestHandler handler = (IRequestHandler)Activator.CreateInstance(handlerInfo.RequestHandlerType); 63 | 64 | if (handler != null && handler.IsReentrant) 65 | { 66 | handlerInfo.ReentrantInstance = handler; 67 | } 68 | 69 | return handler; 70 | } 71 | 72 | private static string ParseCommandFromUrl(string url) 73 | { 74 | if (string.IsNullOrEmpty(url) || url[0] != '/' || url.Length < 2) 75 | { 76 | return string.Empty; 77 | } 78 | 79 | int endPosition = url.IndexOfAny(new char[] { '/', '?' }, 1); 80 | 81 | if (endPosition < 0) 82 | { 83 | return url.Substring(1); 84 | } 85 | else 86 | { 87 | return url.Substring(1, endPosition - 1); 88 | } 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /Revalee.Service/EsePersistence/Esent.Interop.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/Revalee.Service/EsePersistence/Esent.Interop.dll -------------------------------------------------------------------------------- /Revalee.Service/EsePersistence/Esent.Interop.pdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/Revalee.Service/EsePersistence/Esent.Interop.pdb -------------------------------------------------------------------------------- /Revalee.Service/ILoggingProvider.cs: -------------------------------------------------------------------------------- 1 | using System.Diagnostics; 2 | 3 | namespace Revalee.Service 4 | { 5 | public interface ILoggingProvider 6 | { 7 | void WriteEntry(string message, TraceEventType severity); 8 | 9 | void Flush(); 10 | } 11 | } -------------------------------------------------------------------------------- /Revalee.Service/IPartialMatchDictionary.cs: -------------------------------------------------------------------------------- 1 | namespace Revalee.Service 2 | { 3 | internal interface IPartialMatchDictionary 4 | { 5 | ValueType Match(KeyType key); 6 | } 7 | } -------------------------------------------------------------------------------- /Revalee.Service/IRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal interface IRequestHandler 6 | { 7 | void Process(HttpListenerRequest request, HttpListenerResponse response); 8 | 9 | bool IsReentrant { get; } 10 | } 11 | } -------------------------------------------------------------------------------- /Revalee.Service/ITaskPersistenceProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Revalee.Service 5 | { 6 | public interface ITaskPersistenceProvider : IDisposable 7 | { 8 | void Open(string connectionString); 9 | 10 | void Close(); 11 | 12 | IEnumerable ListAllTasks(); 13 | 14 | IEnumerable ListTasksDueBetween(DateTime startTime, DateTime endTime); 15 | 16 | RevaleeTask GetTask(Guid callbackId); 17 | 18 | void AddTask(RevaleeTask task); 19 | 20 | void RemoveTask(RevaleeTask task); 21 | } 22 | } -------------------------------------------------------------------------------- /Revalee.Service/ITelemetryAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal interface ITelemetryAdapter : IDisposable 6 | { 7 | void DecrementAwaitingTasksValue(); 8 | 9 | void IncrementAwaitingTasksValue(); 10 | 11 | void RecordAcceptedRequest(); 12 | 13 | void RecordFailedCallback(); 14 | 15 | void RecordRejectedRequest(); 16 | 17 | void RecordSuccessfulCallback(); 18 | 19 | void RecordWaitTime(TimeSpan waitTime); 20 | 21 | void SetAwaitingTasksValue(int count); 22 | } 23 | } -------------------------------------------------------------------------------- /Revalee.Service/Installation Notes.txt: -------------------------------------------------------------------------------- 1 |  2 | To install the service: 3 | Revalee.Service.exe -install 4 | 5 | To remove the service: 6 | Revalee.Service.exe -uninstall 7 | 8 | On newer versions of Windows the HTTP listener will need to be registered. 9 | 10 | To register the listener on Windows 7 (or later) & Windows Server 2008 (or later): 11 | netsh http add urlacl url=http://+:46200/ user="NT AUTHORITY\NETWORK SERVICE" 12 | 13 | 14 | To register the listener on Windows Vista: 15 | netsh http add urlacl url=http://+:46200/ user="NT AUTHORITY\NETWORK SERVICE" 16 | netsh http add iplisten ipaddress=0.0.0.0:46200 17 | 18 | 19 | To register the listener on Windows XP & Windows Server 2003: 20 | httpcfg set urlacl /u http://+:46200/ /a D:(A;;GX;;;S-1-5-20) 21 | httpcfg set iplisten -i 0.0.0.0:46200 22 | 23 | 24 | Notes: 25 | netsh.exe can be found in the %Windows%\System32 directory. 26 | httpcfg.exe can either be found in the %Windows%\System32 directory or installed from the Windows XP Service Pack 2 Support Tools. 27 | The NT AUTHORITY\NETWORK SERVICE account needs file read rights to the directory where Revalee.Service.exe resides. 28 | 29 | 30 | To use the service: 31 | 32 | Execute an HTTP "PUT" against http://127.0.0.1:46200/Schedule?CallbackTime={time_string}&CallbackUrl={full_url_to_callback} 33 | 34 | {time_string} can be any legally formatted RFC 1123 or ISO 8601 date/time string. If no timezone information is present, then UTC time will be assumed. 35 | 36 | For example: http://127.0.0.1:46200/Schedule?CallbackTime=1999-12-31T23:59:59Z&CallbackUrl=http://127.0.0.1:80/Timers/WakeUp.aspx 37 | -------------------------------------------------------------------------------- /Revalee.Service/License.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Sage Analytic Technologies, LLC 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Revalee.Service/ListenerPrefix.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal class ListenerPrefix 6 | { 7 | private const string _HostPlusReplacementTag = "____plus____"; 8 | private const string _HostAsteriskReplacementTag = "____asterisk____"; 9 | 10 | private readonly string _PrefixString; 11 | 12 | public string Scheme { get; private set; } 13 | 14 | public string Host { get; private set; } 15 | 16 | public int Port { get; private set; } 17 | 18 | public ListenerPrefix(string urlPrefix) 19 | { 20 | if (string.IsNullOrWhiteSpace(urlPrefix)) 21 | { 22 | throw new ArgumentNullException("urlPrefix"); 23 | } 24 | 25 | try 26 | { 27 | urlPrefix = EnsureEndingSlash(urlPrefix); 28 | 29 | var prefixUri = new Uri(EncodeHostWithinUrl(urlPrefix), UriKind.Absolute); 30 | 31 | if (!IsAbsoluteUrl(prefixUri)) 32 | { 33 | throw new FormatException("Uri is not a valid listener prefix URL."); 34 | } 35 | 36 | _PrefixString = urlPrefix; 37 | this.Scheme = prefixUri.Scheme; 38 | this.Host = DecodeHost(prefixUri.Host); 39 | this.Port = prefixUri.Port; 40 | } 41 | catch (Exception ex) 42 | { 43 | throw new ArgumentException(ex.Message, "urlPrefix"); 44 | } 45 | } 46 | 47 | private ListenerPrefix(string prefixString, string scheme, string host, int port) 48 | { 49 | _PrefixString = prefixString; 50 | this.Scheme = scheme; 51 | this.Host = host; 52 | this.Port = port; 53 | } 54 | 55 | public static bool TryCreate(string urlPrefix, out ListenerPrefix prefix) 56 | { 57 | if (!string.IsNullOrWhiteSpace(urlPrefix)) 58 | { 59 | Uri prefixUri = null; 60 | urlPrefix = EnsureEndingSlash(urlPrefix); 61 | 62 | if (Uri.TryCreate(EncodeHostWithinUrl(urlPrefix), UriKind.Absolute, out prefixUri)) 63 | { 64 | if (!IsAbsoluteUrl(prefixUri)) 65 | { 66 | prefix = null; 67 | return false; 68 | } 69 | 70 | prefix = new ListenerPrefix(urlPrefix, prefixUri.Scheme, DecodeHost(prefixUri.Host), prefixUri.Port); 71 | return true; 72 | } 73 | } 74 | 75 | prefix = null; 76 | return false; 77 | } 78 | 79 | public override string ToString() 80 | { 81 | return _PrefixString; 82 | } 83 | 84 | private static string DecodeHost(string host) 85 | { 86 | if (host.Equals(_HostPlusReplacementTag, StringComparison.Ordinal)) 87 | { 88 | return "+"; 89 | } 90 | else if (host.Equals(_HostAsteriskReplacementTag, StringComparison.Ordinal)) 91 | { 92 | return "*"; 93 | } 94 | else 95 | { 96 | return host; 97 | } 98 | } 99 | 100 | private static string EncodeHostWithinUrl(string url) 101 | { 102 | int schemeDelimiterIndex = url.IndexOf(Uri.SchemeDelimiter, 0, StringComparison.Ordinal); 103 | 104 | if (schemeDelimiterIndex > 0) 105 | { 106 | int hostIndex = schemeDelimiterIndex + Uri.SchemeDelimiter.Length; 107 | 108 | if (url.Length > (hostIndex + 2)) 109 | { 110 | char firstHostCharacter = url[hostIndex]; 111 | 112 | if (firstHostCharacter == '+') 113 | { 114 | return string.Concat(url.Substring(0, hostIndex), _HostPlusReplacementTag, url.Substring(hostIndex + 1)); 115 | } 116 | else if (firstHostCharacter == '*') 117 | { 118 | return string.Concat(url.Substring(0, hostIndex), _HostAsteriskReplacementTag, url.Substring(hostIndex + 1)); 119 | } 120 | } 121 | } 122 | 123 | return url; 124 | } 125 | 126 | private static string EnsureEndingSlash(string urlPrefix) 127 | { 128 | if (urlPrefix[urlPrefix.Length - 1] != '/') 129 | { 130 | urlPrefix += '/'; 131 | } 132 | 133 | return urlPrefix; 134 | } 135 | 136 | private static bool IsAbsoluteUrl(Uri uri) 137 | { 138 | return (uri.IsAbsoluteUri && !uri.IsFile && !uri.IsUnc && string.IsNullOrEmpty(uri.Query) && string.IsNullOrEmpty(uri.UserInfo)); 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /Revalee.Service/NullTaskPersistenceProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal class NullTaskPersistenceProvider : ITaskPersistenceProvider 7 | { 8 | public void Open(string connectionString) 9 | { 10 | return; 11 | } 12 | 13 | public void Close() 14 | { 15 | return; 16 | } 17 | 18 | public RevaleeTask GetTask(Guid callbackId) 19 | { 20 | return null; 21 | } 22 | 23 | public void AddTask(RevaleeTask Task) 24 | { 25 | return; 26 | } 27 | 28 | public void RemoveTask(RevaleeTask Task) 29 | { 30 | return; 31 | } 32 | 33 | public IEnumerable ListAllTasks() 34 | { 35 | return new RevaleeTask[] { }; 36 | } 37 | 38 | public IEnumerable ListTasksDueBetween(DateTime startTime, DateTime endTime) 39 | { 40 | return new RevaleeTask[] { }; 41 | } 42 | 43 | public void Dispose() 44 | { 45 | GC.SuppressFinalize(this); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /Revalee.Service/NullTelemetryAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal class NullTelemetryAdapter : ITelemetryAdapter 6 | { 7 | public void DecrementAwaitingTasksValue() 8 | { 9 | } 10 | 11 | public void IncrementAwaitingTasksValue() 12 | { 13 | } 14 | 15 | public void RecordAcceptedRequest() 16 | { 17 | } 18 | 19 | public void RecordFailedCallback() 20 | { 21 | } 22 | 23 | public void RecordRejectedRequest() 24 | { 25 | } 26 | 27 | public void RecordSuccessfulCallback() 28 | { 29 | } 30 | 31 | public void RecordWaitTime(TimeSpan waitTime) 32 | { 33 | } 34 | 35 | public void SetAwaitingTasksValue(int count) 36 | { 37 | } 38 | 39 | public void Dispose() 40 | { 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /Revalee.Service/PerformanceCounterTelemetryAdapter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal class PerformanceCounterTelemetryAdapter : ITelemetryAdapter 7 | { 8 | // Awaiting Tasks : NumberOfItems32 9 | private readonly PerformanceCounter _AwaitingTasks; 10 | 11 | // Average Wait Time : AverageCount64 (ms) 12 | private readonly PerformanceCounter _AverageWaitTime; 13 | 14 | // Average Wait Time base : AverageBase 15 | private readonly PerformanceCounter _AverageWaitTimeBase; 16 | 17 | // Requests/Sec : RateOfCountsPerSecond32 18 | private readonly PerformanceCounter _RequestsPerSecond; 19 | 20 | // Accepted Requests : NumberOfItems64 21 | private readonly PerformanceCounter _AcceptedRequests; 22 | 23 | // Rejected Requests : NumberOfItems64 24 | private readonly PerformanceCounter _RejectedRequests; 25 | 26 | // Callbacks/Sec : RateOfCountsPerSecond32 27 | private readonly PerformanceCounter _CallbacksPerSecond; 28 | 29 | // Successful Callbacks : NumberOfItems64 30 | private readonly PerformanceCounter _SuccessfulCallbacks; 31 | 32 | // Failed Callbacks : NumberOfItems64 33 | private readonly PerformanceCounter _FailedCallbacks; 34 | 35 | public PerformanceCounterTelemetryAdapter() 36 | { 37 | string categoryName = Supervisor.Configuration.ServiceName; 38 | _AwaitingTasks = new PerformanceCounter(categoryName, "Awaiting Tasks", false); 39 | _AverageWaitTime = new PerformanceCounter(categoryName, "Average Wait Time", false); 40 | _AverageWaitTimeBase = new PerformanceCounter(categoryName, "Average Wait Time base", false); 41 | _RequestsPerSecond = new PerformanceCounter(categoryName, "Requests/Sec", false); 42 | _AcceptedRequests = new PerformanceCounter(categoryName, "Accepted Requests", false); 43 | _RejectedRequests = new PerformanceCounter(categoryName, "Rejected Requests", false); 44 | _CallbacksPerSecond = new PerformanceCounter(categoryName, "Callbacks/Sec", false); 45 | _SuccessfulCallbacks = new PerformanceCounter(categoryName, "Successful Callbacks", false); 46 | _FailedCallbacks = new PerformanceCounter(categoryName, "Failed Callbacks", false); 47 | } 48 | 49 | public void SetAwaitingTasksValue(int count) 50 | { 51 | _AwaitingTasks.RawValue = count; 52 | } 53 | 54 | public void IncrementAwaitingTasksValue() 55 | { 56 | _AwaitingTasks.Increment(); 57 | } 58 | 59 | public void DecrementAwaitingTasksValue() 60 | { 61 | _AwaitingTasks.Decrement(); 62 | } 63 | 64 | public void RecordAcceptedRequest() 65 | { 66 | _AcceptedRequests.Increment(); 67 | _RequestsPerSecond.Increment(); 68 | } 69 | 70 | public void RecordRejectedRequest() 71 | { 72 | _RejectedRequests.Increment(); 73 | _RequestsPerSecond.Increment(); 74 | } 75 | 76 | public void RecordSuccessfulCallback() 77 | { 78 | _SuccessfulCallbacks.Increment(); 79 | _CallbacksPerSecond.Increment(); 80 | } 81 | 82 | public void RecordFailedCallback() 83 | { 84 | _FailedCallbacks.Increment(); 85 | _CallbacksPerSecond.Increment(); 86 | } 87 | 88 | public void RecordWaitTime(TimeSpan waitTime) 89 | { 90 | _AverageWaitTime.IncrementBy(Convert.ToInt64(waitTime.TotalMilliseconds)); 91 | _AverageWaitTimeBase.Increment(); 92 | } 93 | 94 | public void Dispose() 95 | { 96 | Dispose(true); 97 | GC.SuppressFinalize(this); 98 | } 99 | 100 | protected virtual void Dispose(bool disposing) 101 | { 102 | if (disposing) 103 | { 104 | _AwaitingTasks.Dispose(); 105 | _AverageWaitTime.Dispose(); 106 | _AverageWaitTimeBase.Dispose(); 107 | _RequestsPerSecond.Dispose(); 108 | _AcceptedRequests.Dispose(); 109 | _RejectedRequests.Dispose(); 110 | _CallbacksPerSecond.Dispose(); 111 | _SuccessfulCallbacks.Dispose(); 112 | _FailedCallbacks.Dispose(); 113 | } 114 | } 115 | } 116 | } -------------------------------------------------------------------------------- /Revalee.Service/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.InteropServices; 5 | 6 | [assembly: AssemblyTitle("Revalee.Service")] 7 | [assembly: AssemblyDescription("Makes web requests based on a specified time.")] 8 | [assembly: AssemblyConfiguration("")] 9 | [assembly: AssemblyCompany("Sage Analytic")] 10 | [assembly: AssemblyProduct("Revalee.Service")] 11 | [assembly: AssemblyCopyright("Copyright © 2014, Sage Analytic Technologies, LLC")] 12 | [assembly: AssemblyTrademark("")] 13 | [assembly: AssemblyCulture("")] 14 | [assembly: CLSCompliant(true)] 15 | [assembly: ComVisible(false)] 16 | [assembly: Guid("d6845934-7ec2-4a09-aafc-c73dc94976f4")] 17 | [assembly: NeutralResourcesLanguageAttribute("en-US")] 18 | [assembly: AssemblyVersion("2.0.0")] 19 | [assembly: AssemblyFileVersion("2.3.1")] 20 | [assembly: AssemblyInformationalVersion("2.3.1")] -------------------------------------------------------------------------------- /Revalee.Service/RequestHandlerDirectory.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal class RequestHandlerDirectory : List 6 | { 7 | public void AddHandler(string command) where T : IRequestHandler 8 | { 9 | this.Add(new RequestHandlerMapping(command, typeof(T))); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /Revalee.Service/RequestHandlerMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal class RequestHandlerMapping 6 | { 7 | public RequestHandlerMapping(string command, Type requestHandlerType) 8 | { 9 | if (requestHandlerType == null) 10 | { 11 | throw new ArgumentNullException("requestHandlerType"); 12 | } 13 | 14 | if (!typeof(IRequestHandler).IsAssignableFrom(requestHandlerType)) 15 | { 16 | throw new ArgumentException("Mapped type is not a request handler.", "requestHandlerType"); 17 | } 18 | 19 | this.Command = command ?? string.Empty; 20 | this.RequestHandlerType = requestHandlerType; 21 | } 22 | 23 | public string Command { get; private set; } 24 | 25 | public Type RequestHandlerType { get; private set; } 26 | } 27 | } -------------------------------------------------------------------------------- /Revalee.Service/RequestManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | 5 | namespace Revalee.Service 6 | { 7 | internal class RequestManager : IDisposable 8 | { 9 | private HttpListener _Listener; 10 | private CommandRouter _Router; 11 | private Thread _ListeningThread; 12 | 13 | public RequestManager() 14 | { 15 | if (!HttpListener.IsSupported) 16 | { 17 | throw new InvalidOperationException("Windows XP SP2, Windows Server 2003, or later is required to run this service."); 18 | } 19 | 20 | var requestHandlers = new RequestHandlerDirectory(); 21 | requestHandlers.AddHandler("Schedule"); 22 | requestHandlers.AddHandler("Cancel"); 23 | requestHandlers.AddHandler("Status"); 24 | 25 | _Router = new CommandRouter(requestHandlers); 26 | _ListeningThread = new Thread(new ThreadStart(Listen)); 27 | } 28 | 29 | public void Activate() 30 | { 31 | if (_Listener == null) 32 | { 33 | // Do not commence listening if there are no configured URL prefixes 34 | if (Supervisor.Configuration.ListenerPrefixes.Count == 0) 35 | { 36 | return; 37 | } 38 | 39 | _Listener = new HttpListener(); 40 | 41 | foreach (ListenerPrefix prefix in Supervisor.Configuration.ListenerPrefixes) 42 | { 43 | _Listener.Prefixes.Add(prefix.ToString()); 44 | } 45 | 46 | _Listener.IgnoreWriteExceptions = true; 47 | } 48 | 49 | try 50 | { 51 | if (!_Listener.IsListening) 52 | { 53 | _Listener.Start(); 54 | } 55 | 56 | if (!_ListeningThread.IsAlive) 57 | { 58 | _ListeningThread.Start(); 59 | } 60 | } 61 | catch (HttpListenerException hlex) 62 | { 63 | if (hlex.ErrorCode == 5) 64 | { 65 | throw new InvalidOperationException(string.Format("Administrator has not delegated the right to listen on this url prefix [{0}].", Supervisor.Configuration.ListenerPrefixes[0]), hlex); 66 | } 67 | else 68 | { 69 | throw; 70 | } 71 | } 72 | } 73 | 74 | public void Abort() 75 | { 76 | if (_Listener != null) 77 | { 78 | _Listener.Abort(); 79 | } 80 | } 81 | 82 | public void Deactivate() 83 | { 84 | if (_Listener != null) 85 | { 86 | if (_Listener.IsListening) 87 | { 88 | _Listener.Stop(); 89 | } 90 | } 91 | } 92 | 93 | private void Listen() 94 | { 95 | while (_Listener.IsListening) 96 | { 97 | try 98 | { 99 | IAsyncResult async_result = _Listener.BeginGetContext(new AsyncCallback(ListenerCallback), _Listener); 100 | async_result.AsyncWaitHandle.WaitOne(); 101 | } 102 | catch (HttpListenerException) 103 | { 104 | // This exception is thrown by the listener when it is being shutdown 105 | return; 106 | } 107 | catch (ObjectDisposedException) 108 | { 109 | // This exception is thrown when the listener is disposed while listening 110 | return; 111 | } 112 | catch (ApplicationException) 113 | { 114 | // This exception is thrown by the listener when already commanded to stop 115 | return; 116 | } 117 | } 118 | } 119 | 120 | public void ListenerCallback(IAsyncResult result) 121 | { 122 | HttpListener listener = (HttpListener)result.AsyncState; 123 | HttpListenerContext context = null; 124 | 125 | try 126 | { 127 | context = listener.EndGetContext(result); 128 | } 129 | catch (HttpListenerException) 130 | { 131 | // This exception is thrown by the listener when it is being shutdown 132 | return; 133 | } 134 | catch (ObjectDisposedException) 135 | { 136 | // This exception is thrown when the listener is disposed while listening 137 | return; 138 | } 139 | 140 | if (context == null) 141 | { 142 | return; 143 | } 144 | 145 | ProcessRequest(context.Request, context.Response); 146 | } 147 | 148 | private void ProcessRequest(HttpListenerRequest request, HttpListenerResponse response) 149 | { 150 | response.AddHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0"); 151 | response.AddHeader("Pragma", "no-cache"); 152 | 153 | IRequestHandler handler = AssignRequestHandler(request); 154 | 155 | if (handler != null) 156 | { 157 | handler.Process(request, response); 158 | } 159 | else 160 | { 161 | FinalizeIgnoredResponse(response, 404, "Resource Not Found"); 162 | } 163 | } 164 | 165 | private IRequestHandler AssignRequestHandler(HttpListenerRequest request) 166 | { 167 | return _Router.MapHandler(request.RawUrl); 168 | } 169 | 170 | private void FinalizeIgnoredResponse(HttpListenerResponse response, int statusCode, string statusDescription) 171 | { 172 | try 173 | { 174 | response.StatusCode = statusCode; 175 | response.StatusDescription = statusDescription; 176 | } 177 | finally 178 | { 179 | response.Close(); 180 | } 181 | } 182 | 183 | public void Dispose() 184 | { 185 | Dispose(true); 186 | GC.SuppressFinalize(this); 187 | } 188 | 189 | protected virtual void Dispose(bool disposing) 190 | { 191 | if (disposing) 192 | { 193 | if (_Listener != null) 194 | { 195 | try 196 | { 197 | if (_Listener.IsListening) 198 | { 199 | _Listener.Stop(); 200 | } 201 | 202 | ((IDisposable)_Listener).Dispose(); 203 | } 204 | catch (ObjectDisposedException) 205 | { 206 | } 207 | } 208 | 209 | if (_ListeningThread != null && _ListeningThread.IsAlive) 210 | { 211 | _ListeningThread.Abort(); 212 | _ListeningThread = null; 213 | } 214 | } 215 | } 216 | } 217 | } -------------------------------------------------------------------------------- /Revalee.Service/RetryHeuristics.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal static class RetryHeuristics 7 | { 8 | private static readonly ScheduledDictionary _FailedCallbackLog = new ScheduledDictionary(); 9 | private static readonly TimeSpan _FailureTimeWindow = TimeSpan.FromHours(6.0); 10 | 11 | public static TimeSpan OnRetryableFailure(Uri callbackUrl) 12 | { 13 | if (callbackUrl == null) 14 | { 15 | throw new ArgumentNullException("callbackUrl"); 16 | } 17 | 18 | _FailedCallbackLog.RemoveAllOverdue(); 19 | int retryIndex = _FailedCallbackLog.AddOrReplace(callbackUrl.Authority, (key) => 0, (key, oldValue, oldDue) => oldValue + 1, (key, newValue, oldDue) => DateTime.UtcNow.Add(_FailureTimeWindow)); 20 | return AssignDelayInterval(retryIndex); 21 | } 22 | 23 | public static void OnSuccess(Uri callbackUrl) 24 | { 25 | if (callbackUrl == null) 26 | { 27 | throw new ArgumentNullException("callbackUrl"); 28 | } 29 | 30 | int dummy; 31 | _FailedCallbackLog.TryRemove(callbackUrl.Authority, out dummy); 32 | } 33 | 34 | private static TimeSpan AssignDelayInterval(int retryIndex) 35 | { 36 | if (retryIndex < 0) 37 | { 38 | return TimeSpan.Zero; 39 | } 40 | 41 | IList retryIntervals = Supervisor.Configuration.RetryIntervals; 42 | 43 | if (retryIndex >= retryIntervals.Count) 44 | { 45 | return retryIntervals[retryIntervals.Count - 1]; 46 | } 47 | else 48 | { 49 | return retryIntervals[retryIndex]; 50 | } 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /Revalee.Service/Revalee.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace Revalee.Service 2 | { 3 | partial class Revalee 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | // 32 | // Revalee 33 | // 34 | this.AutoLog = false; 35 | this.CanPauseAndContinue = true; 36 | this.CanShutdown = true; 37 | this.ServiceName = "Revalee.Service"; 38 | 39 | } 40 | 41 | #endregion 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Revalee.Service/Revalee.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Reflection; 4 | using System.ServiceProcess; 5 | 6 | namespace Revalee.Service 7 | { 8 | partial class Revalee : ServiceBase 9 | { 10 | private const int SERVICE_ACCEPT_PRESHUTDOWN = 0x100; 11 | private const int SERVICE_CONTROL_PRESHUTDOWN = 0xf; 12 | 13 | public Revalee() 14 | { 15 | InitializeComponent(); 16 | this.ServiceName = Supervisor.Configuration.ServiceName; 17 | 18 | if (Environment.OSVersion.Version.Major >= 6) 19 | { 20 | AcceptPreShutdown(); 21 | } 22 | } 23 | 24 | protected override void OnContinue() 25 | { 26 | Supervisor.Resume(); 27 | base.OnContinue(); 28 | } 29 | 30 | protected override void OnPause() 31 | { 32 | Supervisor.Pause(); 33 | base.OnPause(); 34 | } 35 | 36 | protected override void OnStart(string[] args) 37 | { 38 | try 39 | { 40 | Supervisor.Start(); 41 | base.OnStart(args); 42 | } 43 | catch (Exception ex) 44 | { 45 | Supervisor.LogException(ex, TraceEventType.Critical, "Service failed to start"); 46 | 47 | using (var controller = new ServiceController(this.ServiceName)) 48 | { 49 | controller.Stop(); 50 | } 51 | 52 | this.ExitCode = 1; 53 | } 54 | } 55 | 56 | protected override void OnStop() 57 | { 58 | try 59 | { 60 | Supervisor.Stop(); 61 | } 62 | catch { } 63 | 64 | base.OnStop(); 65 | } 66 | 67 | protected override void OnShutdown() 68 | { 69 | try 70 | { 71 | Supervisor.Shutdown(); 72 | } 73 | catch { } 74 | 75 | base.OnShutdown(); 76 | 77 | this.Stop(); 78 | } 79 | 80 | protected override void OnCustomCommand(int command) 81 | { 82 | if (command == SERVICE_CONTROL_PRESHUTDOWN) 83 | { 84 | try 85 | { 86 | Supervisor.Shutdown(); 87 | } 88 | catch { } 89 | } 90 | 91 | base.OnCustomCommand(command); 92 | 93 | this.Stop(); 94 | } 95 | 96 | [MTAThread] 97 | public static void Main() 98 | { 99 | string[] commandLineArgs = Environment.GetCommandLineArgs(); 100 | 101 | if (commandLineArgs == null || commandLineArgs.Length == 1) 102 | { 103 | if (Environment.UserInteractive) 104 | { 105 | RunAsInteractive(); 106 | } 107 | else 108 | { 109 | RunAsService(); 110 | } 111 | } 112 | else if (commandLineArgs.Length == 2) 113 | { 114 | if (commandLineArgs[1] == "-?" || string.Equals(commandLineArgs[1], "-help", StringComparison.OrdinalIgnoreCase)) 115 | { 116 | InteractiveExecution.Help(); 117 | } 118 | else if (string.Equals(commandLineArgs[1], "-install", StringComparison.OrdinalIgnoreCase)) 119 | { 120 | try 121 | { 122 | var installer = new CommandLineInstaller(); 123 | installer.Install(); 124 | } 125 | catch (Exception ex) 126 | { 127 | Console.WriteLine(); 128 | Console.WriteLine(ex.Message); 129 | Environment.ExitCode = 1; 130 | } 131 | } 132 | else if (string.Equals(commandLineArgs[1], "-uninstall", StringComparison.OrdinalIgnoreCase)) 133 | { 134 | try 135 | { 136 | var installer = new CommandLineInstaller(); 137 | installer.Uninstall(); 138 | } 139 | catch (Exception ex) 140 | { 141 | Console.WriteLine(); 142 | Console.WriteLine(ex.Message); 143 | Environment.ExitCode = 1; 144 | } 145 | } 146 | else if (string.Equals(commandLineArgs[1], "-interactive", StringComparison.OrdinalIgnoreCase)) 147 | { 148 | RunAsInteractive(); 149 | } 150 | else if (string.Equals(commandLineArgs[1], "-export", StringComparison.OrdinalIgnoreCase)) 151 | { 152 | TaskExporter.DumpToConsole(); 153 | } 154 | else 155 | { 156 | InteractiveExecution.Help(); 157 | Console.WriteLine(); 158 | Console.WriteLine("Error: Invalid switch `{0}`.", commandLineArgs[1]); 159 | Environment.ExitCode = 1; 160 | } 161 | } 162 | else 163 | { 164 | InteractiveExecution.Help(); 165 | Console.WriteLine(); 166 | Console.WriteLine("Error: Invalid command line arguments."); 167 | Environment.ExitCode = 1; 168 | } 169 | } 170 | 171 | private static void RunAsService() 172 | { 173 | try 174 | { 175 | AppDomain.CurrentDomain.UnhandledException += ServiceCallbackUnhandledExceptionHandler; 176 | ServiceBase.Run(new Revalee()); 177 | } 178 | catch (Exception ex) 179 | { 180 | try 181 | { 182 | Supervisor.LogException(ex, TraceEventType.Critical, "Service encountered a critical error"); 183 | } 184 | catch 185 | { } 186 | } 187 | } 188 | 189 | private static void RunAsInteractive() 190 | { 191 | try 192 | { 193 | InteractiveExecution.Run(); 194 | } 195 | catch (Exception ex) 196 | { 197 | try 198 | { 199 | Supervisor.LogException(ex, TraceEventType.Critical, "Service terminating on error"); 200 | } 201 | catch { } 202 | 203 | Environment.ExitCode = 1; 204 | } 205 | } 206 | 207 | private static void ServiceCallbackUnhandledExceptionHandler(object sender, UnhandledExceptionEventArgs args) 208 | { 209 | try 210 | { 211 | Supervisor.LogException((Exception)args.ExceptionObject, TraceEventType.Critical, "Service encountered a critical error"); 212 | } 213 | catch 214 | { } 215 | } 216 | 217 | private void AcceptPreShutdown() 218 | { 219 | FieldInfo acceptedCommandsFieldInfo = typeof(ServiceBase).GetField("acceptedCommands", BindingFlags.Instance | BindingFlags.NonPublic); 220 | if (acceptedCommandsFieldInfo != null) 221 | { 222 | acceptedCommandsFieldInfo.SetValue(this, ((int)acceptedCommandsFieldInfo.GetValue(this)) | SERVICE_ACCEPT_PRESHUTDOWN); 223 | } 224 | } 225 | } 226 | } -------------------------------------------------------------------------------- /Revalee.Service/Revalee.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/Revalee.Service/Revalee.ico -------------------------------------------------------------------------------- /Revalee.Service/RevaleeServiceInstaller.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace Revalee.Service 2 | { 3 | partial class RevaleeServiceInstaller 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Component Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.serviceInstaller1 = new System.ServiceProcess.ServiceInstaller(); 32 | this.serviceProcessInstaller1 = new System.ServiceProcess.ServiceProcessInstaller(); 33 | this.performanceCounterInstaller1 = new System.Diagnostics.PerformanceCounterInstaller(); 34 | // 35 | // serviceInstaller1 36 | // 37 | this.serviceInstaller1.Description = "Makes web requests based on a specified time."; 38 | this.serviceInstaller1.DisplayName = "Revalee Service"; 39 | this.serviceInstaller1.ServiceName = "Revalee.Service"; 40 | this.serviceInstaller1.ServicesDependedOn = new string[] { 41 | "HTTP"}; 42 | this.serviceInstaller1.StartType = System.ServiceProcess.ServiceStartMode.Automatic; 43 | // 44 | // serviceProcessInstaller1 45 | // 46 | this.serviceProcessInstaller1.Account = System.ServiceProcess.ServiceAccount.NetworkService; 47 | this.serviceProcessInstaller1.Password = null; 48 | this.serviceProcessInstaller1.Username = null; 49 | // 50 | // performanceCounterInstaller1 51 | // 52 | this.performanceCounterInstaller1.CategoryHelp = "Telemetry to monitor the activity of the Revalee Service."; 53 | this.performanceCounterInstaller1.CategoryName = "Revalee.Service"; 54 | this.performanceCounterInstaller1.CategoryType = System.Diagnostics.PerformanceCounterCategoryType.SingleInstance; 55 | this.performanceCounterInstaller1.Counters.AddRange(new System.Diagnostics.CounterCreationData[] { 56 | new System.Diagnostics.CounterCreationData("Awaiting Tasks", "Total number of tasks waiting to be processed.", System.Diagnostics.PerformanceCounterType.NumberOfItems32), 57 | new System.Diagnostics.CounterCreationData("Requests/Sec", "Number of incoming requests per second.", System.Diagnostics.PerformanceCounterType.RateOfCountsPerSecond32), 58 | new System.Diagnostics.CounterCreationData("Accepted Requests", "Total number of accepted requests.", System.Diagnostics.PerformanceCounterType.NumberOfItems64), 59 | new System.Diagnostics.CounterCreationData("Rejected Requests", "Total number of rejected requests.", System.Diagnostics.PerformanceCounterType.NumberOfItems64), 60 | new System.Diagnostics.CounterCreationData("Callbacks/Sec", "Number of callbacks processed per second.", System.Diagnostics.PerformanceCounterType.RateOfCountsPerSecond32), 61 | new System.Diagnostics.CounterCreationData("Successful Callbacks", "Total number of successfully processed callbacks.", System.Diagnostics.PerformanceCounterType.NumberOfItems64), 62 | new System.Diagnostics.CounterCreationData("Failed Callbacks", "Total number of callbacks that failed during processing.", System.Diagnostics.PerformanceCounterType.NumberOfItems64), 63 | new System.Diagnostics.CounterCreationData("Average Wait Time", "Average number of milliseconds that a callback waits for processing after its sch" + 64 | "eduled time.", System.Diagnostics.PerformanceCounterType.AverageCount64), 65 | new System.Diagnostics.CounterCreationData("Average Wait Time base", "Base for Average Wait Time", System.Diagnostics.PerformanceCounterType.AverageBase)}); 66 | // 67 | // RevaleeServiceInstaller 68 | // 69 | this.Installers.AddRange(new System.Configuration.Install.Installer[] { 70 | this.serviceInstaller1, 71 | this.serviceProcessInstaller1, 72 | this.performanceCounterInstaller1}); 73 | 74 | } 75 | 76 | #endregion 77 | 78 | internal System.ServiceProcess.ServiceInstaller serviceInstaller1; 79 | internal System.ServiceProcess.ServiceProcessInstaller serviceProcessInstaller1; 80 | internal System.Diagnostics.PerformanceCounterInstaller performanceCounterInstaller1; 81 | } 82 | } -------------------------------------------------------------------------------- /Revalee.Service/RevaleeServiceInstaller.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel; 2 | using System.Configuration.Install; 3 | 4 | namespace Revalee.Service 5 | { 6 | [RunInstaller(true)] 7 | public partial class RevaleeServiceInstaller : Installer 8 | { 9 | public RevaleeServiceInstaller() 10 | { 11 | InitializeComponent(); 12 | 13 | if (!string.Equals(this.serviceInstaller1.ServiceName, Supervisor.Configuration.ServiceName, System.StringComparison.Ordinal)) 14 | { 15 | RenameService(Supervisor.Configuration.ServiceName); 16 | } 17 | } 18 | 19 | private void RenameService(string serviceName) 20 | { 21 | this.serviceInstaller1.ServiceName = serviceName; 22 | this.serviceInstaller1.DisplayName = serviceName.Replace('.', ' ').Replace('_', ' '); 23 | this.performanceCounterInstaller1.CategoryName = serviceName; 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Revalee.Service/RevaleeTask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Revalee.Service 5 | { 6 | public class RevaleeTask : IComparable 7 | { 8 | private readonly DateTime _CallbackTime; 9 | private readonly Uri _CallbackUrl; 10 | private readonly DateTime _CreatedTime; 11 | private readonly Guid _CallbackId; 12 | private readonly string _AuthorizationCipher; 13 | private int _AttemptsRemaining; 14 | 15 | public RevaleeTask(DateTime callbackTime, Uri callbackUrl) 16 | : this(callbackTime, callbackUrl, 0, null) 17 | { 18 | } 19 | 20 | public RevaleeTask(DateTime callbackTime, Uri callbackUrl, int retryCount) 21 | : this(callbackTime, callbackUrl, retryCount, null) 22 | { 23 | } 24 | 25 | public RevaleeTask(DateTime callbackTime, Uri callbackUrl, int retryCount, string authorizationCipher) 26 | { 27 | _CreatedTime = DateTime.UtcNow; 28 | 29 | if (callbackUrl == null) 30 | { 31 | throw new ArgumentNullException("callbackUrl"); 32 | } 33 | 34 | _CallbackTime = callbackTime; 35 | _CallbackUrl = callbackUrl; 36 | _CallbackId = Guid.NewGuid(); 37 | 38 | if (retryCount < 1) 39 | { 40 | _AttemptsRemaining = 1; 41 | } 42 | else 43 | { 44 | _AttemptsRemaining = 1 + retryCount; 45 | } 46 | 47 | if (!string.IsNullOrEmpty(authorizationCipher)) 48 | { 49 | _AuthorizationCipher = authorizationCipher; 50 | } 51 | } 52 | 53 | private RevaleeTask(DateTime callbackTime, Uri callbackUrl, DateTime createdTime, Guid callbackId, int attemptsRemaining, string authorizationCipher) 54 | { 55 | if (callbackUrl == null) 56 | { 57 | throw new ArgumentNullException("callbackUrl"); 58 | } 59 | 60 | if (Guid.Empty.Equals(callbackId)) 61 | { 62 | throw new ArgumentNullException("callbackId"); 63 | } 64 | 65 | _CallbackTime = callbackTime; 66 | _CallbackUrl = callbackUrl; 67 | _CreatedTime = createdTime; 68 | _CallbackId = callbackId; 69 | 70 | if (attemptsRemaining < 0) 71 | { 72 | _AttemptsRemaining = 0; 73 | } 74 | else 75 | { 76 | _AttemptsRemaining = attemptsRemaining; 77 | } 78 | 79 | if (!string.IsNullOrEmpty(authorizationCipher)) 80 | { 81 | _AuthorizationCipher = authorizationCipher; 82 | } 83 | } 84 | 85 | public static RevaleeTask Revive(DateTime callbackTime, Uri callbackUrl, DateTime createdTime, Guid callbackId, int attemptsRemaining, string authorizationCipher) 86 | { 87 | return new RevaleeTask(callbackTime, callbackUrl, createdTime, callbackId, attemptsRemaining, authorizationCipher); 88 | } 89 | 90 | public DateTime CallbackTime 91 | { 92 | get { return _CallbackTime; } 93 | } 94 | 95 | public Uri CallbackUrl 96 | { 97 | get { return _CallbackUrl; } 98 | } 99 | 100 | public DateTime CreatedTime 101 | { 102 | get { return _CreatedTime; } 103 | } 104 | 105 | public Guid CallbackId 106 | { 107 | get { return _CallbackId; } 108 | } 109 | 110 | public int AttemptsRemaining 111 | { 112 | get { return _AttemptsRemaining; } 113 | } 114 | 115 | public string AuthorizationCipher 116 | { 117 | get { return _AuthorizationCipher; } 118 | } 119 | 120 | public bool AttemptCallback() 121 | { 122 | return Interlocked.Decrement(ref _AttemptsRemaining) >= 0; 123 | } 124 | 125 | public int CompareTo(RevaleeTask other) 126 | { 127 | if (other == null) 128 | { 129 | throw new ArgumentNullException("other"); 130 | } 131 | 132 | if (_CallbackId.Equals(other.CallbackId)) 133 | { 134 | return 0; 135 | } 136 | 137 | int returnValue = _CallbackTime.CompareTo(other.CallbackTime); 138 | 139 | if (returnValue == 0) 140 | { 141 | returnValue = _CreatedTime.CompareTo(other.CreatedTime); 142 | if (returnValue == 0) 143 | { 144 | returnValue = -1; 145 | } 146 | } 147 | 148 | return returnValue; 149 | } 150 | } 151 | } -------------------------------------------------------------------------------- /Revalee.Service/RevaleeUrlAuthorization.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Security.Cryptography; 5 | using System.Threading; 6 | 7 | namespace Revalee.Service 8 | { 9 | public class RevaleeUrlAuthorization 10 | { 11 | private Uri _UrlPrefix; 12 | private List _AuthorizedAddresses = new List(); 13 | private int _RetryCount; 14 | 15 | public RevaleeUrlAuthorization(string urlPrefix, string fromAddresses, string retryCount) 16 | { 17 | if (string.IsNullOrWhiteSpace(urlPrefix)) 18 | { 19 | throw new ArgumentNullException("urlPrefix"); 20 | } 21 | 22 | _UrlPrefix = new Uri(urlPrefix, UriKind.Absolute); 23 | 24 | LoadFromAddresses(fromAddresses); 25 | 26 | if (!string.IsNullOrEmpty(retryCount)) 27 | { 28 | if (!int.TryParse(retryCount, out _RetryCount)) 29 | { 30 | _RetryCount = 0; 31 | } 32 | 33 | if (_RetryCount < 0 || _RetryCount > 100) 34 | { 35 | throw new ArgumentOutOfRangeException("retryCount"); 36 | } 37 | } 38 | } 39 | 40 | private void LoadFromAddresses(string value) 41 | { 42 | if (!string.IsNullOrWhiteSpace(value)) 43 | { 44 | string[] addressList = value.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); 45 | if (addressList.Length > 0) 46 | { 47 | foreach (string address in addressList) 48 | { 49 | string trimmedAddress = address.Trim(); 50 | if (trimmedAddress.Length > 0) 51 | { 52 | IPNetwork ipNetwork = IPNetwork.Parse(trimmedAddress); 53 | if (ipNetwork != null) 54 | { 55 | _AuthorizedAddresses.Add(ipNetwork); 56 | } 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | public bool IsAuthorizedRequestSource(IPAddress address) 64 | { 65 | // Zero authorization entries implies that no address restrictions are required 66 | if (_AuthorizedAddresses.Count == 0) 67 | { 68 | return true; 69 | } 70 | 71 | foreach (IPNetwork authorizedNetwork in _AuthorizedAddresses) 72 | { 73 | if (authorizedNetwork.IsAddressInNetwork(address)) 74 | { 75 | return true; 76 | } 77 | } 78 | 79 | return false; 80 | } 81 | 82 | public Uri UrlPrefix 83 | { 84 | get { return _UrlPrefix; } 85 | } 86 | 87 | public int RetryCount 88 | { 89 | get { return _RetryCount; } 90 | } 91 | 92 | public static void ObfuscateExecutionTime() 93 | { 94 | byte[] milliseconds = new byte[2]; 95 | 96 | using (var rng = new RNGCryptoServiceProvider()) 97 | { 98 | rng.GetBytes(milliseconds); 99 | } 100 | 101 | Thread.Sleep(Math.Abs(milliseconds[0] + milliseconds[1])); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /Revalee.Service/SecuritySettingsConfigSection.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal class SecuritySettingsConfigSection : ConfigurationSection 6 | { 7 | public static SecuritySettingsConfigSection GetConfig() 8 | { 9 | return (SecuritySettingsConfigSection)System.Configuration.ConfigurationManager.GetSection("securitySettings"); 10 | } 11 | 12 | [ConfigurationProperty("urlAuthorizations", IsDefaultCollection = false), ConfigurationCollection(typeof(UrlAuthorizationElementCollection), AddItemName = "authorize")] 13 | public UrlAuthorizationElementCollection UrlAuthorizations 14 | { 15 | get { return (UrlAuthorizationElementCollection)this["urlAuthorizations"]; } 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /Revalee.Service/StatusRequestHandler.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Globalization; 4 | using System.Net; 5 | using System.Reflection; 6 | using System.Text; 7 | 8 | namespace Revalee.Service 9 | { 10 | internal class StatusRequestHandler : IRequestHandler 11 | { 12 | public void Process(HttpListenerRequest request, HttpListenerResponse response) 13 | { 14 | try 15 | { 16 | if (request.HttpMethod != "GET") 17 | { 18 | response.StatusCode = 405; 19 | response.StatusDescription = "Method Not Supported"; 20 | response.Close(); 21 | return; 22 | } 23 | 24 | string version = FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).ProductVersion; 25 | string status = GetStatusDescription(); 26 | string timestamp = DateTime.UtcNow.ToString("s", CultureInfo.InvariantCulture) + "Z"; 27 | 28 | FormatJsonResponse(response, version, status, timestamp); 29 | } 30 | catch (HttpListenerException hlex) 31 | { 32 | Supervisor.LogException(hlex, TraceEventType.Error, request.RawUrl); 33 | 34 | response.StatusCode = 500; 35 | response.StatusDescription = "Error Occurred"; 36 | response.Close(); 37 | } 38 | } 39 | 40 | private static string GetStatusDescription() 41 | { 42 | if (Supervisor.Work.IsOverloaded) 43 | { 44 | return "overloaded"; 45 | } 46 | else if (!Supervisor.IsStarted) 47 | { 48 | return "stopped"; 49 | } 50 | else if (Supervisor.IsPaused) 51 | { 52 | return "paused"; 53 | } 54 | else if (Supervisor.State.AwaitingTaskCount == 0) 55 | { 56 | return "idle"; 57 | } 58 | else 59 | { 60 | return "active"; 61 | } 62 | } 63 | 64 | private static void FormatJsonResponse(HttpListenerResponse response, string version, string status, string timestamp) 65 | { 66 | try 67 | { 68 | response.StatusCode = 200; 69 | response.StatusDescription = "OK"; 70 | response.ContentType = "application/json"; 71 | 72 | var messageBuffer = new StringBuilder(); 73 | messageBuffer.Append("{ \"version\": \""); 74 | messageBuffer.Append(version); 75 | messageBuffer.Append("\", \"status\": \""); 76 | messageBuffer.Append(status); 77 | messageBuffer.Append("\", \"timestamp\": \""); 78 | messageBuffer.Append(timestamp); 79 | messageBuffer.Append("\" }"); 80 | 81 | byte[] binaryPayload = Encoding.UTF8.GetBytes(messageBuffer.ToString()); 82 | response.ContentLength64 = binaryPayload.LongLength; 83 | response.OutputStream.Write(binaryPayload, 0, binaryPayload.Length); 84 | } 85 | finally 86 | { 87 | response.Close(); 88 | } 89 | } 90 | 91 | public bool IsReentrant 92 | { 93 | get { return true; } 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /Revalee.Service/TaskExporter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | 5 | namespace Revalee.Service 6 | { 7 | internal class TaskExporter 8 | { 9 | private TaskExporter() 10 | { 11 | } 12 | 13 | public static void DumpToConsole() 14 | { 15 | var taskList = new SortedList(); 16 | TaskPersistenceSettings persistenceSettings; 17 | 18 | var config = new ConfigurationManager(); 19 | persistenceSettings = config.TaskPersistenceSettings; 20 | 21 | // Load persisted tasks from the persistence provider 22 | ITaskPersistenceProvider persistenceProvider = persistenceSettings.CreateProvider(); 23 | 24 | if (persistenceProvider is NullTaskPersistenceProvider) 25 | { 26 | Console.WriteLine("WARNING: Exporting tasks is not available, because there is no configured task persistence provider."); 27 | return; 28 | } 29 | 30 | try 31 | { 32 | persistenceProvider.Open(persistenceSettings.ConnectionString); 33 | 34 | foreach (RevaleeTask task in persistenceProvider.ListAllTasks()) 35 | { 36 | taskList.Add(task, task); 37 | } 38 | } 39 | finally 40 | { 41 | persistenceProvider.Close(); 42 | } 43 | 44 | // Write to the console 45 | foreach (RevaleeTask task in taskList.Values) 46 | { 47 | Console.WriteLine(string.Format(CultureInfo.InvariantCulture, "{0:s}Z {1}", task.CallbackTime, task.CallbackUrl.OriginalString)); 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Revalee.Service/TaskPersistenceProviderMapping.cs: -------------------------------------------------------------------------------- 1 | using Revalee.Service.EsePersistence; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | namespace Revalee.Service 6 | { 7 | /* Example provider strings: 8 | * 9 | * Microsoft.Isam.Esent 10 | * System.Data.SqlClient 11 | * System.Data.OleDb 12 | * System.Data.Odbc 13 | * System.Data.OracleClient 14 | */ 15 | 16 | internal static class TaskPersistenceProviderMapping 17 | { 18 | private static IDictionary _ProviderMappings = LoadProviderMappings(); 19 | 20 | private static IDictionary LoadProviderMappings() 21 | { 22 | var mappings = new Dictionary(StringComparer.OrdinalIgnoreCase); 23 | mappings.Add("Microsoft.Isam.Esent", typeof(EseTaskPersistenceProvider)); 24 | return mappings; 25 | } 26 | 27 | public static Type MapProvider(string providerName) 28 | { 29 | if (string.IsNullOrWhiteSpace(providerName)) 30 | { 31 | return typeof(NullTaskPersistenceProvider); 32 | } 33 | 34 | try 35 | { 36 | return _ProviderMappings[providerName.Trim()]; 37 | } 38 | catch (KeyNotFoundException) 39 | { 40 | return null; 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /Revalee.Service/TaskPersistenceSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Configuration; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal class TaskPersistenceSettings 7 | { 8 | public TaskPersistenceSettings(ConnectionStringSettings connectionStringSettings) 9 | { 10 | if (connectionStringSettings == null) 11 | { 12 | this.ProviderType = typeof(NullTaskPersistenceProvider); 13 | this.ConnectionString = string.Empty; 14 | } 15 | else 16 | { 17 | string providerName = connectionStringSettings.ProviderName; 18 | Type providerType = TaskPersistenceProviderMapping.MapProvider(providerName); 19 | 20 | if (providerType == null) 21 | { 22 | throw new ConfigurationErrorsException(string.Format("The configured task persistence provider, '{0}', is not supported.", providerName), 23 | connectionStringSettings.ElementInformation.Source, 24 | connectionStringSettings.ElementInformation.LineNumber); 25 | } 26 | 27 | this.ProviderType = providerType; 28 | this.ConnectionString = connectionStringSettings.ConnectionString; 29 | } 30 | } 31 | 32 | public Type ProviderType 33 | { 34 | get; 35 | private set; 36 | } 37 | 38 | public string ConnectionString 39 | { 40 | get; 41 | private set; 42 | } 43 | 44 | public ITaskPersistenceProvider CreateProvider() 45 | { 46 | return (ITaskPersistenceProvider)Activator.CreateInstance(this.ProviderType); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /Revalee.Service/TelemetryManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal class TelemetryManager : ITelemetryAdapter, IDisposable 7 | { 8 | private static readonly ITelemetryAdapter _EmptyAdapter = new NullTelemetryAdapter(); 9 | private ITelemetryAdapter _Adapter; 10 | 11 | public TelemetryManager() 12 | { 13 | _Adapter = _EmptyAdapter; 14 | } 15 | 16 | public void Activate() 17 | { 18 | if (_Adapter == _EmptyAdapter) 19 | { 20 | Activate(new PerformanceCounterTelemetryAdapter()); 21 | } 22 | } 23 | 24 | public void Activate(ITelemetryAdapter telemetryAdapter) 25 | { 26 | if (telemetryAdapter == null) 27 | { 28 | throw new ArgumentNullException("telemetryAdapter"); 29 | } 30 | 31 | if (_Adapter != _EmptyAdapter) 32 | { 33 | _Adapter.Dispose(); 34 | } 35 | 36 | _Adapter = telemetryAdapter; 37 | } 38 | 39 | public void Deactivate() 40 | { 41 | if (_Adapter != _EmptyAdapter) 42 | { 43 | _Adapter.Dispose(); 44 | _Adapter = _EmptyAdapter; 45 | } 46 | } 47 | 48 | public void SetAwaitingTasksValue(int count) 49 | { 50 | _Adapter.SetAwaitingTasksValue(count); 51 | } 52 | 53 | public void IncrementAwaitingTasksValue() 54 | { 55 | _Adapter.IncrementAwaitingTasksValue(); 56 | } 57 | 58 | public void DecrementAwaitingTasksValue() 59 | { 60 | _Adapter.DecrementAwaitingTasksValue(); 61 | } 62 | 63 | public void RecordAcceptedRequest() 64 | { 65 | _Adapter.RecordAcceptedRequest(); 66 | } 67 | 68 | public void RecordRejectedRequest() 69 | { 70 | _Adapter.RecordRejectedRequest(); 71 | } 72 | 73 | public void RecordSuccessfulCallback() 74 | { 75 | _Adapter.RecordSuccessfulCallback(); 76 | } 77 | 78 | public void RecordFailedCallback() 79 | { 80 | _Adapter.RecordFailedCallback(); 81 | } 82 | 83 | public void RecordWaitTime(TimeSpan waitTime) 84 | { 85 | _Adapter.RecordWaitTime(waitTime); 86 | } 87 | 88 | public void Dispose() 89 | { 90 | Dispose(true); 91 | GC.SuppressFinalize(this); 92 | } 93 | 94 | protected virtual void Dispose(bool disposing) 95 | { 96 | if (disposing) 97 | { 98 | if (_Adapter != _EmptyAdapter) 99 | { 100 | _Adapter.Dispose(); 101 | } 102 | } 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /Revalee.Service/TimeManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | 4 | namespace Revalee.Service 5 | { 6 | // This class will fire off time-based events 7 | 8 | internal class TimeManager : IDisposable 9 | { 10 | private const int _ImmediateTimerInterval = 0; 11 | private const int _MaximumTimerIntervalInMilliseconds = 3600000; 12 | 13 | private readonly object _SyncRoot = new object(); 14 | private Timer _InternalTimer; 15 | 16 | public TimeManager() 17 | { 18 | } 19 | 20 | public void Start() 21 | { 22 | if (_InternalTimer == null) 23 | { 24 | lock (_SyncRoot) 25 | { 26 | if (_InternalTimer == null) 27 | { 28 | _InternalTimer = new Timer(new TimerCallback(TimerElapsed), null, _ImmediateTimerInterval, Timeout.Infinite); 29 | } 30 | else 31 | { 32 | _InternalTimer.Change(_ImmediateTimerInterval, Timeout.Infinite); 33 | } 34 | } 35 | } 36 | else 37 | { 38 | _InternalTimer.Change(_ImmediateTimerInterval, Timeout.Infinite); 39 | } 40 | } 41 | 42 | public void Stop() 43 | { 44 | if (_InternalTimer != null) 45 | { 46 | lock (_SyncRoot) 47 | { 48 | if (_InternalTimer != null) 49 | { 50 | _InternalTimer.Dispose(); 51 | _InternalTimer = null; 52 | } 53 | } 54 | } 55 | } 56 | 57 | private void TimerElapsed(object state) 58 | { 59 | Supervisor.Work.Dispatch(); 60 | EnsureContinuation(); 61 | } 62 | 63 | private void EnsureContinuation() 64 | { 65 | DateTime? nextTaskTime = Supervisor.State.NextTaskTime; 66 | if (nextTaskTime.HasValue) 67 | { 68 | SetNextDueTime(nextTaskTime.Value); 69 | } 70 | } 71 | 72 | private void SetNextDueTime(DateTime nextTaskTime) 73 | { 74 | double millisecondsOfDelay = nextTaskTime.Subtract(DateTime.UtcNow).TotalMilliseconds; 75 | 76 | int nextTimerInterval; 77 | 78 | if (millisecondsOfDelay >= int.MaxValue) 79 | { 80 | nextTimerInterval = _MaximumTimerIntervalInMilliseconds; 81 | } 82 | else if (millisecondsOfDelay < int.MinValue) 83 | { 84 | nextTimerInterval = _ImmediateTimerInterval; 85 | } 86 | else 87 | { 88 | nextTimerInterval = (int)millisecondsOfDelay; 89 | } 90 | 91 | if (nextTimerInterval <= _ImmediateTimerInterval) 92 | { 93 | _InternalTimer.Change(_ImmediateTimerInterval, Timeout.Infinite); 94 | } 95 | else if (nextTimerInterval >= _MaximumTimerIntervalInMilliseconds) 96 | { 97 | _InternalTimer.Change(_MaximumTimerIntervalInMilliseconds, Timeout.Infinite); 98 | } 99 | else 100 | { 101 | _InternalTimer.Change(nextTimerInterval + 1, Timeout.Infinite); 102 | } 103 | } 104 | 105 | public void Dispose() 106 | { 107 | Dispose(true); 108 | GC.SuppressFinalize(this); 109 | } 110 | 111 | protected virtual void Dispose(bool disposing) 112 | { 113 | if (disposing) 114 | { 115 | if (_InternalTimer != null) 116 | { 117 | _InternalTimer.Dispose(); 118 | } 119 | } 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /Revalee.Service/TraceListenerLoggingProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Security.Permissions; 4 | 5 | namespace Revalee.Service 6 | { 7 | internal class TraceListenerLoggingProvider : ILoggingProvider 8 | { 9 | private InternalLogHandler _Log = new InternalLogHandler(); 10 | 11 | public void WriteEntry(string message, TraceEventType severity) 12 | { 13 | try 14 | { 15 | _Log.TraceSource.TraceEvent(severity, 0, message); 16 | } 17 | catch (ObjectDisposedException) 18 | { 19 | // ignore trace attempt if the handler is already disposed 20 | } 21 | } 22 | 23 | public void Flush() 24 | { 25 | try 26 | { 27 | _Log.TraceSource.Flush(); 28 | } 29 | catch (ObjectDisposedException) 30 | { 31 | // ignore flush attempt if the handler is already disposed 32 | } 33 | } 34 | 35 | [HostProtection(SecurityAction.LinkDemand, Resources = HostProtectionResource.ExternalProcessMgmt)] 36 | private sealed class InternalLogHandler 37 | { 38 | public InternalLogHandler() 39 | { 40 | this.TraceSource = new TraceSource(System.Reflection.Assembly.GetEntryAssembly().GetName().Name); 41 | AppDomain.CurrentDomain.ProcessExit += new EventHandler(this.CloseOnProcessExit); 42 | } 43 | 44 | private void CloseOnProcessExit(object sender, EventArgs e) 45 | { 46 | AppDomain.CurrentDomain.ProcessExit -= new EventHandler(this.CloseOnProcessExit); 47 | this.TraceSource.Close(); 48 | } 49 | 50 | public TraceSource TraceSource 51 | { 52 | get; 53 | private set; 54 | } 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Revalee.Service/UrlAuthorizationElement.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Configuration; 3 | 4 | namespace Revalee.Service 5 | { 6 | internal class UrlAuthorizationElement : ConfigurationElement 7 | { 8 | [ConfigurationProperty("urlPrefix", IsRequired = true)] 9 | public string UrlPrefix 10 | { 11 | get { return Convert.ToString(this["urlPrefix"]); } 12 | } 13 | 14 | [ConfigurationProperty("fromAddresses", IsRequired = false)] 15 | public string FromAddresses 16 | { 17 | get { return Convert.ToString(this["fromAddresses"]); } 18 | } 19 | 20 | [ConfigurationProperty("retries", DefaultValue = "0", IsRequired = false)] 21 | public string Retries 22 | { 23 | get { return Convert.ToString(this["retries"]); } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /Revalee.Service/UrlAuthorizationElementCollection.cs: -------------------------------------------------------------------------------- 1 | using System.Configuration; 2 | 3 | namespace Revalee.Service 4 | { 5 | internal class UrlAuthorizationElementCollection : ConfigurationElementCollection 6 | { 7 | public UrlAuthorizationElement this[int index] 8 | { 9 | get 10 | { 11 | return (UrlAuthorizationElement)this.BaseGet(index); 12 | } 13 | set 14 | { 15 | if (this.BaseGet(index) != null) 16 | { 17 | this.BaseRemoveAt(index); 18 | } 19 | this.BaseAdd(index, value); 20 | } 21 | } 22 | 23 | protected override ConfigurationElement CreateNewElement() 24 | { 25 | return new UrlAuthorizationElement(); 26 | } 27 | 28 | protected override object GetElementKey(ConfigurationElement element) 29 | { 30 | return ((UrlAuthorizationElement)element).UrlPrefix; 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /Revalee.Service/UrlMatchDictionary.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace Revalee.Service 6 | { 7 | public class UrlMatchDictionary : IPartialMatchDictionary where T : class 8 | { 9 | private readonly object _SyncRoot = new object(); 10 | private readonly Dictionary _UrlList = new Dictionary(); 11 | private IndexedCollection _UrlIndexedCollection = null; 12 | private bool _ReindexingRequired; 13 | 14 | private class IndexedCollection 15 | { 16 | public IndexedCollection(string[] matchKeys, T[] payloads) 17 | { 18 | this.MatchKeys = matchKeys; 19 | this.Payloads = payloads; 20 | } 21 | 22 | public readonly string[] MatchKeys; 23 | public readonly T[] Payloads; 24 | } 25 | 26 | public UrlMatchDictionary() 27 | { 28 | } 29 | 30 | public void Add(Uri urlPrefix, T payload) 31 | { 32 | if (urlPrefix == null) 33 | { 34 | throw new ArgumentNullException("urlPrefix"); 35 | } 36 | 37 | if (payload == null) 38 | { 39 | throw new ArgumentNullException("payload"); 40 | } 41 | 42 | string matchKey = BuildMatchKey(urlPrefix); 43 | 44 | lock (_SyncRoot) 45 | { 46 | try 47 | { 48 | _UrlList.Add(matchKey, payload); 49 | ScheduleIndexing(); 50 | } 51 | catch (ArgumentException) 52 | { 53 | throw new ArgumentException("Duplicate url prefix found.", "urlPrefix"); 54 | } 55 | } 56 | } 57 | 58 | public T Match(Uri url) 59 | { 60 | if (url == null) 61 | { 62 | throw new ArgumentNullException("url"); 63 | } 64 | 65 | string matchKey = BuildMatchKey(url); 66 | if (_UrlIndexedCollection != null) 67 | { 68 | return IndexedMatch(matchKey); 69 | } 70 | else 71 | { 72 | return ScannedMatch(matchKey); 73 | } 74 | } 75 | 76 | private T IndexedMatch(string matchKey) 77 | { 78 | IndexedCollection urlIndex = _UrlIndexedCollection; 79 | 80 | if (urlIndex.MatchKeys.Length == 0) 81 | { 82 | return default(T); 83 | } 84 | 85 | int matchIndex = Array.BinarySearch(urlIndex.MatchKeys, matchKey); 86 | 87 | if (matchIndex < 0) 88 | { 89 | // If the index is negative, it represents the bitwise 90 | // complement of the next larger element in the array. 91 | 92 | // The element before the larger element is a candidate of a partial match 93 | int candidateMatchIndex = (matchIndex ^ -1) - 1; 94 | 95 | if (candidateMatchIndex < 0) 96 | { 97 | return default(T); 98 | } 99 | 100 | if (matchKey.StartsWith(urlIndex.MatchKeys[candidateMatchIndex], StringComparison.Ordinal)) 101 | { 102 | return urlIndex.Payloads[candidateMatchIndex]; 103 | } 104 | 105 | return default(T); 106 | } 107 | else 108 | { 109 | return urlIndex.Payloads[matchIndex]; 110 | } 111 | } 112 | 113 | private T ScannedMatch(string matchKey) 114 | { 115 | lock (_SyncRoot) 116 | { 117 | T exactMatchPayload = default(T); 118 | if (_UrlList.TryGetValue(matchKey, out exactMatchPayload)) 119 | { 120 | return exactMatchPayload; 121 | } 122 | 123 | int bestMatchCount = 0; 124 | string bestMatchKey = null; 125 | int matchMaxIndex = matchKey.Length - 1; 126 | 127 | foreach (string key in _UrlList.Keys) 128 | { 129 | int matchCount = 0; 130 | for (int charIndex = 0; charIndex < key.Length; charIndex++) 131 | { 132 | if (charIndex > matchMaxIndex) 133 | { 134 | matchCount = 0; 135 | break; 136 | } 137 | else if (matchKey[charIndex] == key[charIndex]) 138 | { 139 | matchCount += 1; 140 | } 141 | else 142 | { 143 | matchCount = 0; 144 | break; 145 | } 146 | } 147 | 148 | if (matchCount > bestMatchCount) 149 | { 150 | bestMatchCount = matchCount; 151 | bestMatchKey = key; 152 | if (matchCount == matchMaxIndex) 153 | { 154 | // The best possible match is found 155 | break; 156 | } 157 | } 158 | } 159 | 160 | if (bestMatchKey == null) 161 | { 162 | return default(T); 163 | } 164 | 165 | return _UrlList[bestMatchKey]; 166 | } 167 | } 168 | 169 | private static string BuildMatchKey(Uri url) 170 | { 171 | return url.OriginalString; 172 | } 173 | 174 | private void ScheduleIndexing() 175 | { 176 | if (_ReindexingRequired) 177 | { 178 | return; 179 | } 180 | 181 | _ReindexingRequired = true; 182 | ThreadPool.QueueUserWorkItem(new WaitCallback(BuildIndex)); 183 | } 184 | 185 | private void BuildIndex(object state) 186 | { 187 | if (!_ReindexingRequired) 188 | { 189 | return; 190 | } 191 | 192 | Thread.Sleep(TimeSpan.FromSeconds(1.0)); 193 | 194 | if (!_ReindexingRequired) 195 | { 196 | return; 197 | } 198 | 199 | string[] matchKeys; 200 | T[] payloads; 201 | 202 | lock (_SyncRoot) 203 | { 204 | if (!_ReindexingRequired) 205 | { 206 | return; 207 | } 208 | 209 | _ReindexingRequired = false; 210 | matchKeys = new string[_UrlList.Count]; 211 | payloads = new T[_UrlList.Count]; 212 | _UrlList.Keys.CopyTo(matchKeys, 0); 213 | _UrlList.Values.CopyTo(payloads, 0); 214 | } 215 | 216 | Array.Sort(matchKeys, payloads); 217 | _UrlIndexedCollection = new IndexedCollection(matchKeys, payloads); 218 | } 219 | } 220 | } -------------------------------------------------------------------------------- /Revalee.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.30110.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revalee.Service", "Revalee.Service\Revalee.Service.csproj", "{6A077191-AF52-4307-A29E-AFD561D1BEFB}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revalee.Client", "Revalee.Client\Revalee.Client.csproj", "{459C6481-9884-497B-BE64-06B6B832B073}" 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revalee.SampleSite", "Revalee.SampleSite\Revalee.SampleSite.csproj", "{3813A1B5-AF66-4A9F-B8DB-A9CFFC249744}" 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Revalee.Client.Mvc", "Revalee.Client.Mvc\Revalee.Client.Mvc.csproj", "{AC55639C-BFCF-496C-9019-D3D911706699}" 13 | EndProject 14 | Global 15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 16 | Debug|Any CPU = Debug|Any CPU 17 | Release|Any CPU = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 20 | {6A077191-AF52-4307-A29E-AFD561D1BEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {6A077191-AF52-4307-A29E-AFD561D1BEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {6A077191-AF52-4307-A29E-AFD561D1BEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {6A077191-AF52-4307-A29E-AFD561D1BEFB}.Release|Any CPU.Build.0 = Release|Any CPU 24 | {459C6481-9884-497B-BE64-06B6B832B073}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 25 | {459C6481-9884-497B-BE64-06B6B832B073}.Debug|Any CPU.Build.0 = Debug|Any CPU 26 | {459C6481-9884-497B-BE64-06B6B832B073}.Release|Any CPU.ActiveCfg = Release|Any CPU 27 | {459C6481-9884-497B-BE64-06B6B832B073}.Release|Any CPU.Build.0 = Release|Any CPU 28 | {3813A1B5-AF66-4A9F-B8DB-A9CFFC249744}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {3813A1B5-AF66-4A9F-B8DB-A9CFFC249744}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {3813A1B5-AF66-4A9F-B8DB-A9CFFC249744}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {3813A1B5-AF66-4A9F-B8DB-A9CFFC249744}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {AC55639C-BFCF-496C-9019-D3D911706699}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {AC55639C-BFCF-496C-9019-D3D911706699}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {AC55639C-BFCF-496C-9019-D3D911706699}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {AC55639C-BFCF-496C-9019-D3D911706699}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | EndGlobal 41 | -------------------------------------------------------------------------------- /Revalee.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/Revalee.snk -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Mvc.5.1.1/Content/Web.config.install.xdt: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Mvc.5.1.1/Content/Web.config.uninstall.xdt: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Mvc.5.1.1/Microsoft.AspNet.Mvc.5.1.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.Mvc.5.1.1/Microsoft.AspNet.Mvc.5.1.1.nupkg -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Mvc.5.1.1/Microsoft.AspNet.Mvc.5.1.1.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.Mvc 5 | 5.1.1 6 | Microsoft ASP.NET MVC 7 | Microsoft 8 | Microsoft 9 | http://www.microsoft.com/web/webpi/eula/aspnetcomponent_rtw_ENU.htm 10 | http://www.asp.net/mvc 11 | true 12 | This package contains the runtime assemblies for ASP.NET MVC. ASP.NET MVC gives you a powerful, patterns-based way to build dynamic websites that enables a clean separation of concerns and that gives you full control over markup. 13 | This package contains the runtime assemblies for ASP.NET MVC. 14 | Please visit http://go.microsoft.com/fwlink/?LinkId=389866 to view the release notes. 15 | © Microsoft Corporation. All rights reserved. 16 | en-US 17 | Microsoft AspNet Mvc AspNetMvc 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Mvc.5.1.1/lib/net45/System.Web.Mvc.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.Mvc.5.1.1/lib/net45/System.Web.Mvc.dll -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Razor.3.1.1/Microsoft.AspNet.Razor.3.1.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.Razor.3.1.1/Microsoft.AspNet.Razor.3.1.1.nupkg -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Razor.3.1.1/Microsoft.AspNet.Razor.3.1.1.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.Razor 5 | 3.1.1 6 | Microsoft ASP.NET Razor 7 | Microsoft 8 | Microsoft 9 | http://www.microsoft.com/web/webpi/eula/aspnetcomponent_rtw_ENU.htm 10 | http://www.asp.net/web-pages 11 | true 12 | This package contains the runtime assemblies for ASP.NET Web Pages. ASP.NET Web Pages and the new Razor syntax provide a fast, terse, clean and lightweight way to combine server code with HTML to create dynamic web content. 13 | This package contains the runtime assemblies for ASP.NET Web Pages. 14 | Please visit http://go.microsoft.com/fwlink/?LinkId=389866 to view the release notes. 15 | © Microsoft Corporation. All rights reserved. 16 | en-US 17 | Microsoft AspNet WebPages AspNetWebPages Razor 18 | 19 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.Razor.3.1.1/lib/net45/System.Web.Razor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.Razor.3.1.1/lib/net45/System.Web.Razor.dll -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/Content/Web.config.install.xdt: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/Content/Web.config.uninstall.xdt: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 7 | 8 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/Microsoft.AspNet.WebPages.3.1.1.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.WebPages.3.1.1/Microsoft.AspNet.WebPages.3.1.1.nupkg -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/Microsoft.AspNet.WebPages.3.1.1.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.AspNet.WebPages 5 | 3.1.1 6 | Microsoft ASP.NET Web Pages 7 | Microsoft 8 | Microsoft 9 | http://www.microsoft.com/web/webpi/eula/aspnetcomponent_rtw_ENU.htm 10 | http://www.asp.net/web-pages 11 | true 12 | This package contains core runtime assemblies shared between ASP.NET MVC and ASP.NET Web Pages. 13 | This package contains core runtime assemblies shared between ASP.NET MVC and ASP.NET Web Pages. 14 | Please visit http://go.microsoft.com/fwlink/?LinkId=389866 to view the release notes. 15 | © Microsoft Corporation. All rights reserved. 16 | en-US 17 | Microsoft AspNet WebPages AspNetWebPages 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.Helpers.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.Helpers.dll -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.Deployment.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.Deployment.dll -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.Razor.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.Razor.dll -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.Razor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | System.Web.WebPages.Razor 5 | 6 | 7 | 8 | 9 | A strongly-typed resource class, for looking up localized strings, etc. 10 | 11 | 12 | 13 | 14 | Returns the cached ResourceManager instance used by this class. 15 | 16 | 17 | 18 | 19 | Overrides the current thread's CurrentUICulture property for all 20 | resource lookups using this strongly typed resource class. 21 | 22 | 23 | 24 | 25 | Looks up a localized string similar to Value cannot be null or an empty string.. 26 | 27 | 28 | 29 | 30 | Looks up a localized string similar to Value must be between {0} and {1}.. 31 | 32 | 33 | 34 | 35 | Looks up a localized string similar to Value must be a value from the "{0}" enumeration.. 36 | 37 | 38 | 39 | 40 | Looks up a localized string similar to Value must be greater than {0}.. 41 | 42 | 43 | 44 | 45 | Looks up a localized string similar to Value must be greater than or equal to {0}.. 46 | 47 | 48 | 49 | 50 | Looks up a localized string similar to Value must be less than {0}.. 51 | 52 | 53 | 54 | 55 | Looks up a localized string similar to Value must be less than or equal to {0}.. 56 | 57 | 58 | 59 | 60 | Looks up a localized string similar to Value cannot be an empty string. It must either be null or a non-empty string.. 61 | 62 | 63 | 64 | 65 | For unit testing. 66 | 67 | 68 | 69 | 70 | For unit testing 71 | 72 | 73 | 74 | 75 | Adds a namespace to the global imports list for this AppDomain 76 | 77 | 78 | IMPORTANT: ALL uses of WebPageRazorHost (and derived classes) within the same AppDomain will share this list. 79 | Therefore this method should only be used in runtime scenarios, not in design-time scenarios where the Razor 80 | data structures for multiple files and projects may be shared within a single AppDomain. 81 | 82 | The namespace to add to the global imports list. 83 | 84 | 85 | 86 | A strongly-typed resource class, for looking up localized strings, etc. 87 | 88 | 89 | 90 | 91 | Returns the cached ResourceManager instance used by this class. 92 | 93 | 94 | 95 | 96 | Overrides the current thread's CurrentUICulture property for all 97 | resource lookups using this strongly typed resource class. 98 | 99 | 100 | 101 | 102 | Looks up a localized string similar to Could not determine the code language for "{0}".. 103 | 104 | 105 | 106 | 107 | Looks up a localized string similar to Could not locate Razor Host Factory type: {0}. 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.AspNet.WebPages.3.1.1/lib/net45/System.Web.WebPages.dll -------------------------------------------------------------------------------- /packages/Microsoft.Web.Infrastructure.1.0.0.0/Microsoft.Web.Infrastructure.1.0.0.0.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.Web.Infrastructure.1.0.0.0/Microsoft.Web.Infrastructure.1.0.0.0.nupkg -------------------------------------------------------------------------------- /packages/Microsoft.Web.Infrastructure.1.0.0.0/Microsoft.Web.Infrastructure.1.0.0.0.nuspec: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Microsoft.Web.Infrastructure 5 | 1.0.0.0 6 | Microsoft.Web.Infrastructure 7 | Microsoft 8 | Microsoft 9 | http://go.microsoft.com/fwlink/?LinkID=214339 10 | http://www.asp.net/ 11 | https://download-codeplex.sec.s-msft.com/Download?ProjectName=aspnetwebstack&DownloadId=360555 12 | false 13 | This package contains the Microsoft.Web.Infrastructure assembly that lets you dynamically register HTTP modules at run time. 14 | This package contains the Microsoft.Web.Infrastructure assembly that lets you dynamically register HTTP modules at run time. 15 | ASPNETWEBPAGES 16 | 17 | -------------------------------------------------------------------------------- /packages/Microsoft.Web.Infrastructure.1.0.0.0/lib/net40/Microsoft.Web.Infrastructure.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SageAnalytic/Revalee/cccbccb1fa5915a231279af8de78df20f1aad5f4/packages/Microsoft.Web.Infrastructure.1.0.0.0/lib/net40/Microsoft.Web.Infrastructure.dll -------------------------------------------------------------------------------- /packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | --------------------------------------------------------------------------------