Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Mike Chambers 636 posts 1253 karma points c-trib
    Nov 17, 2011 @ 17:01
    Mike Chambers
    0

    scheduled admin task..

    Is there scope in the API to add our own events to the backend scheduling, for instance the publish on this date routinue?

    I have an admin task that needs performing daily, and don't really want to have to expose a public url to use the scheduler available in the umbracoSettings.config file.

  • Rodion Novoselov 694 posts 859 karma points
    Nov 18, 2011 @ 10:27
    Rodion Novoselov
    0

    Hi, Mike. As far as I investigated it some time ago there's no such inbuilt functionality. I used a trick that probably could help - creating a document type and binding something to its publishing event then you just create a document of this type and set its 'publish at' property.

  • Mike Chambers 636 posts 1253 karma points c-trib
    Nov 18, 2011 @ 10:36
    Mike Chambers
    0

    did some digging myself, and couldn't find anything, just looking now at basing something on the trick of using the a sliding cache for automated scheduling on an interval http://www.codeproject.com/KB/aspnet/ASPNETService.aspx

    or maybe cachedependency on a file to monitor for a change...

  • Richard Soeteman 4046 posts 12899 karma points MVP 2x
    Nov 18, 2011 @ 10:45
    Richard Soeteman
    0

    You can use my taskschedule package http://our.umbraco.org/projects/developer-tools/taskscheduler then the public url will be stored inj the database instead of config file.

    Cheers,

    Richard

  • Rodion Novoselov 694 posts 859 karma points
    Nov 18, 2011 @ 10:48
    Rodion Novoselov
    0

    By the way, the cache trick like referenced above is actually used in umbraco itself. It's possible to schedule some code by putting a fake cache item with some expiration time and attach code to its CacheItemRemoved event so that it will be executed later after timespan expiration. As far as I know from the source code it's the way that Umbraco schedules log truncation.

  • Mike Chambers 636 posts 1253 karma points c-trib
    Nov 18, 2011 @ 11:00
    Mike Chambers
    0

    Thanks Richard, but I'm trying to avoid any use of a public url (just in case it's exposed)

    Rodion, good to know that I'm using a method already utilised in the core... only down side I see to it will be that cache only going to be running if the app pool is up, and as we're in a clustered cloud host app pool uptime is very stringently monitored... might require a separate ping to keep the site alive! although calling a dummy page on cache expiration might also keep the app pool alive.

  • Rodion Novoselov 694 posts 859 karma points
    Nov 18, 2011 @ 12:16
    Rodion Novoselov
    0

    Unfortunately, you cannot avoid the dependency on application pool running out of the architecture of asp.net web applications. Another possible pattern to use for a custom implementation of a scheduler is the one that e.g. DotNetNuke uses. It has a comon custom HttpModule that catches every BeginRequest events then checks scheduled tasks and executes those that are scheduled for execution to that time. There's another drawback in it since execution depends on existance of an incoming http request, but if a task is not strictly sensitive to the exact time of execution this pattern is also has a chance to be used.

  • Mike Chambers 636 posts 1253 karma points c-trib
    Nov 18, 2011 @ 12:33
    Mike Chambers
    0

    Here's my take...

    requires custom Global.ascx and deletion of app_global.aspx.dll and the addition of scheduleCacheReset.aspx [add to the ubracoreservedurls] (although this could just as well be real page in umbraco, or a urlredirected url to some content - just needs a unique url for the cache reset)

     

    public class Global : umbraco.Global
        {
            private const string DummyCacheItemKey = "GagaGuguGigi";
            protected static string DummyUrl;
     
            protected override void Application_Start(object sender, EventArgs e)
            {
                base.Application_Start(sender, e);
                // Insert your startup logic here
                RegisterCacheEntry();
                
            }
     
            protected override void Application_BeginRequest(Object sender, EventArgs e)
            {
                HttpApplication app = (HttpApplication)sender;
                HttpContext context = app.Context;
               
                if (DummyUrl == null)
                {
                    // Attempt to peform first request initialization
                    DummyUrl = FirstRequestInitialisation.Initialise(context);
                }
     
                // If the dummy page is hit, then it means we want to add another item in cache
                if (HttpContext.Current.Request.Url.ToString() == DummyUrl)
                {
                    // Add the item in cache and when succesful, do the work.
                    RegisterCacheEntry();
                }
            }
     
     
            class FirstRequestInitialisation
            {
                private static string host = null;
     
                private static Object s_lock = new Object();
     
                // Initialise only on the first request
                public static string Initialise(HttpContext context)
                {
                    if (string.IsNullOrEmpty(host))
                    {
                        lock (s_lock)
                        {
                            if (string.IsNullOrEmpty(host))
                            {
                                Uri uri = HttpContext.Current.Request.Url;
                                //host = uri.Scheme + Uri.SchemeDelimiter + uri.Host + ":" + uri.Port;
                                host = uri.Scheme + Uri.SchemeDelimiter + uri.Host + "/scheduleCacheReset.aspx";
                                Log.Add(LogTypes.Custom, -1, "FirstInitialise" + host);
                            }
                        }
                    }
     
                    return host;
                }
            }
     
     
            /// 
            /// Register a cache entry which expires in 1 minute and gives us a callback.
            /// 
            /// 
            private void RegisterCacheEntry()
            {
                // Prevent duplicate key addition
                if (null != HttpContext.Current.Cache[DummyCacheItemKey]) return;
                Log.Add(LogTypes.Custom, -1, "Scheduler Cache Item reset");
                HttpContext.Current.Cache.Add(DummyCacheItemKey, "Test", null, DateTime.MaxValue, TimeSpan.FromMinutes(5), CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(CacheItemRemovedCallback));
            }
     
            /// 
            /// Callback method which gets invoked whenever the cache entry expires.
            /// We can do our "service" works here.
            /// 
            ///
            ///
            ///
            public void CacheItemRemovedCallback(string key,object value, CacheItemRemovedReason reason)
            {
                // Do the service works
                DoWork();
     
                // We need to register another cache item which will expire again in one
                // minute. However, as this callback occurs without any HttpContext, we do not
                // have access to HttpContext and thus cannot access the Cache object. The
                // only way we can access HttpContext is when a request is being processed which
                // means a webpage is hit. So, we need to simulate a web page hit and then 
                // add the cache item.
                HitPage();
            }
     
            /// 
            /// Hits a local webpage in order to add another expiring item in cache
            /// 
            private void HitPage()
            {
                System.Net.WebClient client = new System.Net.WebClient();
                client.DownloadData(DummyUrl);
            }
     
            /// 
            /// Asynchronously do the 'service' works
            /// 
            private void DoWork()
            {
                //Log.Add(LogTypes.Custom, -1, "Running as: " + WindowsIdentity.GetCurrent().Name);
     
                //DoSomeFileWritingStuff();
                //DoSomeDatabaseOperation();
                //DoSomeEmailSendStuff();
                //DoSomeMSMQStuff();
                //ExecuteQueuedJobs();
     
            }
        }
  • Mike Chambers 636 posts 1253 karma points c-trib
    Nov 18, 2011 @ 13:17
    Mike Chambers
    0

    Updated to remove the need for that dummy url.... just now looks for a guid in the querystring. (could make it more secure by resetting the guid on each application recycle)

    public class Global : umbraco.Global
        {
            private const string DummyCacheItemKey = "E4161A68-11D9-11E1-8852-2D874824019B";
            protected static string DummyUrl;
     
            protected override void Application_Start(object sender, EventArgs e)
            {
                base.Application_Start(sender, e);
                // Insert your startup logic here
                RegisterCacheEntry();
                
            }
     
            protected override void Application_BeginRequest(Object sender, EventArgs e)
            {
                base.Application_BeginRequest(sender, e);
                HttpApplication app = (HttpApplication)sender;
                HttpContext context = app.Context;
               
                if (DummyUrl == null)
                {
                    // Attempt to peform first request initialization
                    DummyUrl = FirstRequestInitialisation.Initialise(context);
                }
     
                // If the dummy page (one with a ?guid=XXX) is hit, then it means we want to add another item in cache
                try
                {
                    if (HttpContext.Current.Request.QueryString["guid"].ToString() == DummyCacheItemKey)
                    {
                        // Add the item in cache and when succesful, do the work.
                        RegisterCacheEntry();
                    }
                }
                catch { }
            }
     
     
            class FirstRequestInitialisation
            {
                private static string host = null;
     
                private static Object s_lock = new Object();
     
                // Initialise only on the first request
                public static string Initialise(HttpContext context)
                {
                    if (string.IsNullOrEmpty(host))
                    {
                        lock (s_lock)
                        {
                            if (string.IsNullOrEmpty(host))
                            {
                                Uri uri = HttpContext.Current.Request.Url;
                                host = uri.Scheme + Uri.SchemeDelimiter + uri.Host + "/?guid=" + DummyCacheItemKey;                           
                            }
                        }
                    }
     
                    return host;
                }
            }
     
     
            ///
            /// Register a cache entry which expires in 1 minute and gives us a callback.
            ///
            ///
            private void RegisterCacheEntry()
            {
                // Prevent duplicate key addition
                if (null != HttpContext.Current.Cache[DummyCacheItemKey]) return;
                Log.Add(LogTypes.Custom, -1, "Scheduler Cache Item reset");
                HttpContext.Current.Cache.Add(DummyCacheItemKey, "Test", null, DateTime.MaxValue, TimeSpan.FromMinutes(5), CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(CacheItemRemovedCallback));
            }
     
            ///
            /// Callback method which gets invoked whenever the cache entry expires.
            /// We can do our "service" works here.
            ///
            ///
            ///
            ///
            public void CacheItemRemovedCallback(string key,object value, CacheItemRemovedReason reason)
            {
                // Do the service works
                DoWork();
     
                // We need to register another cache item which will expire again in one
                // minute. However, as this callback occurs without any HttpContext, we do not
                // have access to HttpContext and thus cannot access the Cache object. The
                // only way we can access HttpContext is when a request is being processed which
                // means a webpage is hit. So, we need to simulate a web page hit and then 
                // add the cache item.
                HitPage();
            }
     
            /// 
            /// Hits a local webpage in order to add another expiring item in cache
            /// 
            private void HitPage()
            {
                System.Net.WebClient client = new System.Net.WebClient();
                client.DownloadData(DummyUrl);
            }
     
            /// 
            /// Asynchronously do the 'service' works
            /// 
            private void DoWork()
            {
                //Log.Add(LogTypes.Custom, -1, "Running as: " + WindowsIdentity.GetCurrent().Name);
     
                //DoSomeFileWritingStuff();
                //DoSomeDatabaseOperation();
                //DoSomeEmailSendStuff();
                //DoSomeMSMQStuff();
                //ExecuteQueuedJobs();
     
            }
        }
Please Sign in or register to post replies

Write your reply to:

Draft