Support for Long Running Operations using the MOSS Publishing Infrastructure - sharepoint

I have used the Long Running Operation capability within the Publishing Infrastructure within MOSS (SharePoint) in the past, and am curious if anyone knows if this is a supported technique for having custom long running operations within SharePoint.
When using this technique, you inherit from Microsoft.SharePoint.Publishing.Internal.LongRunningOperationJob which seems to indicate that it is not supported for custom use, but I seem to recall (maybe I dreamt?) that long running processes was a marketed feature of MOSS.
Any ideas?

SharePoint Timer Jobs are described in MSDN Journal April edition
http://msdn.microsoft.com/en-us/magazine/dd569748.aspx

Good question Kirk! I have recently also experienced the need to implement long running jobs in SharePoint. But the LongRunningOperationJob was not an option as it also needed to work with a plain WSS 3.0 deployment. I simply ended up spawning a new thread from the Web request and redirecting to an ASPX page with an AJAX enabled progress bar updating itself every other second. It works perfectly well and can run as long as needed. Only downside is that an IISRESET will kill it for good. Another possibility could be to implement long running jobs using a custom SharePoint Timer Job.

SPLongOperation is a very simple way to do a long running operation. Much simpler than the Publishing.LongRunningOperationJob and uses the same infrastructure.

Ever woundered how microsoft create the nice longrunning process windows works in SharePoint 2007?
SPLongOperation is the class to use. It has 2 important methods
Begin and End;
All your code that runs for a long time is placed between begin and end.
Below is a sample class.
It is almost to simple and just works :-)
using System;
using System.Web;
using System.Web.UI.WebControls;
using Microsoft.SharePoint;
namespace CreateLongOperation
{
public class LongRun : System.Web.UI.Page
{
protected Button buttonOk;
protected void Page_Load(object sender, EventArgs e)
{
buttonOk.Click += new EventHandler(buttonOk_Click);
}
void buttonOk_Click(object sender, EventArgs e)
{
SPLongOperation operation = new SPLongOperation(this.Page);
operation.Begin();
// do long operation code here...
System.Threading.Thread.Sleep(6000);
operation.End("http://sps/_layouts/Mynewpage.aspx");
}
}
}

Related

Why my custom C# extention does not execute when deployed to Spotfire WebPlauer/Consumer

I have a simple custom Add-in that just displays a message to the user.
namespace GeorgiSpotfireCustomExtention
{
public class GeorgiEvent : CustomApplicationEventHandler
{
protected override void OnApplicationInstanceCreated(AnalysisApplication application)
{
base.OnApplicationInstanceCreated(application);
MessageBox.Show("On Application Instance Created");
}
}
}
That is my CustomAddIn class:
public sealed class CustomAddIn : AddIn
{
// Override methods in this class to register your extensions.
protected override void RegisterApplicationEventHandlers(ApplicationEventHandlerRegistrar registrar)
{
base.RegisterApplicationEventHandlers(registrar);
registrar.Register(new GeorgiEvent());
}
}
I am just trying to learn the package deployment process. When I am running it locally - in the installed Spotfire Analyst client it displays the message just fine:
However, when I package the extention, add it to the server (via the "Deployments & Packages" section, adding the "spk" file and then saving the area, the message is not shown when I try to open a document in the WebPlayer/Consumer.
Notes: I am choosing "TIBCO Spotfire Any Client" for my intended client in the Package Builder when building the spk file.
from the Spotfire Wiki (emphasis mine):
WinForms graphical user interface is a component of the .NET Framework and not something supplied by Tibco Spotfire. It's not recommended to implement solutions using Forms, but sometimes it could be handy when debugging. There is no commitment that it will work in future versions of the Analyst client. Forms are not supported on the Web Player.
the example listed on the wiki is for IronPython, but presumably the same holds true for C# extensions.
Correct. My assumption, and I don’t really know a lot about .NET, so this is not absolute, is that the form is rendered on the machine executing the code. In the case of your example above, the dialog would pop on the Node Manager host. If you’re really set on using an alert like this, you can accomplish it in JavaScript with an ‘alert()’. There is probably a way to render dialogues o in the web client too, but I don’t know it offhand.

How to add EventSource to a web application

We finally got EventSource and ElasticSearch correctly configured in our service fabric cluster. Now that we have that we want to add EventSources to our web applications that interact with our service fabric applications so that we can view all events (application logs) in one location and filter / query via Kibana.
Our issue seems to be related to the differences between a service fabric app which is an exe and a .NET 4.6 (not .net CORE) web app which is stateless. In service Fabric we place the using statement that instantiates the pipeline in Program.cs and set an infinite sleep.
private static void Main()
{
try
{
using (var diagnosticsPipeline = ServiceFabricDiagnosticPipelineFactory.CreatePipeline("CacheApp-CacheAPI-DiagnosticsPipeline"))
{
ServiceEventSource.Current.ServiceTypeRegistered(Process.GetCurrentProcess().Id, typeof(Endpoint).Name);
// Prevents this host process from terminating so services keeps running.
Thread.Sleep(Timeout.Infinite);
}
How do I do this in a web app? This is the pipeline code we are using for a non ServiceFabric implementation of the EventSource. This is what we are using:
using (var pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json"))
{
IEnumerable ie = System.Diagnostics.Tracing.EventSource.GetSources();
ServiceEventSource.Current.Message("initialize eventsource");
}
We are able to see the pipeline and send events to ElasticSearch from within the using statement but not outside of it. So the question is:
how/where do we place our pipeline using statement for a web app?
Do we need to instantiate and destroy the pipeline that every time we log or is there a way to reuse that pipeline across the stateless web events? It would seem like that would be very expensive and hurt performance. Maybe we can we cache a pipeline?
That’s the jist, let me know if you need clarification. I see lots of doco out there for client apps but not much for web apps.
Thanks,
Greg
UPDATE WITH SOLUTION CODE
DiagnosticPipeline pipeline;
protected void Application_Start(Object sender, EventArgs e)
{
try
{
pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json");
IEnumerable ie = System.Diagnostics.Tracing.EventSource.GetSources();
AppEventSource.Current.Message("initialize eventsource");
}
}
protected void Application_End(Object sender, EventArgs e)
{
pipeline.Dispose();
}
Assuming ASP.NET Core the simplest way to initialize EventFlow pipeline would be in the Program.cs Main() method, for example:
public static void Main(string[] args)
{
using (var pipeline = DiagnosticPipelineFactory.CreatePipeline("eventFlowConfig.json"))
{
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
host.Run();
}
}
This takes advantage of the fact that host.Run() will block until the server is shut down, and so the pipeline will exist during the time when requests are received and served.
Depending on the web framework you use things might vary. E.g. if the one you use offers "setup" and "cleanup" hooks, you could create a diagnostic pipeline during setup phase (and store a reference to it in some member variable), then dispose of it during cleanup phase. For example, in ASP.NET classic you'd put the code in global.asax.cs and leverage Application_OnStart and Application_OnEnd methods. See Application Instances, Application Events, and Application State in ASP.NET for details.
Creating a pipeline instance every time a request is served is quite inefficient, like you said. There is really no good reason to do that.

Add WebRole.cs - and have it called - in an existing ASP.NET MVC site converted to web role

I have an ASP.NET MVC 4 site running perfectly well in an Azure WebRole. The ASP.NET MVC project was started on its own, after which I added an Azure Cloud Service project to the solution and added the ASP.NET project/site as one of the 'roles' of the service (so it shows up in the 'Roles' folder).
My problem is that I would like to have working a WebRole.cs file within the ASP.NET MVC project, but no matter what I've tried to do, it appears that when deployed, it just never gets called. OnStart and the override of Run (which I know, must never leave the loop) -- these just apparently never get called.
But if you startup a new CloudService project and add, at that time from the start, an ASP.NET MVC project, it automatically has a WebRole.cs file in it, so my guess is that I need to configure something somewhere for the WebRole.cs (actually speaking, the WebRole class, which inherits RoleEntryPoint) to get called. What might that be?
using System;
using System.Web;
//using Microsoft.WindowsAzure.StorageClient;
using System.Diagnostics;
using System.Threading;
namespace Us.WebUI
{
public class WebRole : Microsoft.WindowsAzure.ServiceRuntime.RoleEntryPoint
{
public override bool OnStart()
{
return true; //return base.OnStart(); // CALL THIS???
}
public override void Run()
{
while (true) {
Thread.Sleep(TimeSpan.FromSeconds(30));
try {
EmailFuncs.SendEmailToUs("An email from our WebRole?????", "Email me this, email me that.");
}
catch { }
}
}
}
}
UPDATE: Thanks, the question has been answered. But I will add: On doing this, while it clearly was working (fully deployed and in emulator), that suddenly I was having problems doing a full publish of the site. After a azure publish took 3 hours:
Verifying storage account 'xyz'... > Uploading Package... > - Updating... [stayed here for 3 hours], it failed with this error: The server encountered an internal error. Please retry the request. So one thing I was wondering is, did I need to override OnStop in WebRole.cs?
UPDATE 2: Those previous problems were fixed, and had nothing to do with this issue. Actually, I've learned this: If you ever have any warnings generated in your build, Azure often will not work with them even when they don't cause problems locally or in other hosts. Since then, I've been much more studious to tackling build warnings (but critical to this is turning off with warning codes the many warning types you want to ignore!).
Adding a class to your Web Project which inherits from RoleEntryPoint is sufficient, it should just work. Did you try setting a breakpoint in the emulator?
What you might be experiencing is that EmailFuncs.SendEmailToUs requires info from the app/web.config and that this info is not available. You need to know that your WebRole class runs in a different process (not your web application), meaning it's not using your web.config. If you want the WebRole.cs to read info from the configuration file, you'll need to add these settings in WaIISHost.exe.config

Ninject dependency injection in SharePoint Timer Job

I have successfully implemented an enterprise SharePoint solution using Ninject dependency injection and other infrastructure such as NLog logging etc using an Onion architecture. With a HttpModule as an Composition Root for the injection framework, it works great for normal web requests:
public class SharePointNinjectHttpModule: IHttpModule, IDisposable
{
private readonly HttpApplication _httpApplication;
public void Init(HttpApplication context)
{
if (context == null) throw new ArgumentException("context");
Ioc.Container = IocContainerFactory.CreateContainer();
}
public void Dispose()
{
if(_httpApplication == null) return;
_httpApplication.Dispose();
Ioc.Container.Dispose();
}
}
The CreateContainer method loads the Ninject modules from a separate class library and my ioc container is abstracted.
For normal web application requests I used a shared static class for the injector called Ioc. The UI layer has a MVP pattern implementation. E.g in the aspx page the presenter is constructed as follows:
presenter = Ioc.Container.Get<SPPresenter>(new Ninject.Parameters.ConstructorArgument("view", this));
I'm still reliant on a Ninject reference for the parameters. Is there any way to abstract this, other than mapping a lot of methods in a interface? Can't I just pass in simple types for arguments?
The injection itself works great, however my difficulty comes in when using external processes such as SharePoint Timer Jobs. It would obviously be a terrible idea to reuse the ioc container from here, so it needs to bootstrap the dependencies itself. In addition, it needs to load the configuration from the web application pool, not the admin web application. Else the job would only be able to run on the application server. This way the job can run on any web server, and your SharePoint feature only has to deploy configurations etc. to the web apllication.
Here is the execute method of my timer job, it opens the associated web application configuration and passes it to the logging service (nlog) and reads it's configuration from the external web config service. I have written code that reads a custom section in the configuration file and initializes the NLog logging infrastructure.
public override void Execute(Guid contentDbId)
{
try
{
using (var ioc = IocContainerFactory.CreateContainer())
{
// open configuration from web application
var configService = ioc.Get<IConfigService>(new ConstructorArgument("webApplicationName", this.WebApplication.Name));
// get logging service and set with web application configuration
var logginService = ioc.Get<ILoggingService>();
logginService.SetConfiguration(configService);
// reapply bindings
ioc.Rebind<IConfigService>().ToConstant(configService);
ioc.Rebind<ILoggingService>().ToConstant(logginService);
try
{
logginService.Info("Test Job started.");
// use services etc...
var productService = ioc.Get<IProductService>();
var products = productService.GetProducts(5);
logginService.Info("Got products: " + products.Count() + " Config from web application: " + configService.TestConfigSetting);
logginService.Info("Test Job completed.");
}
catch (Exception exception)
{
logginService.Error(exception);
}
}
}
catch (Exception exception)
{
EventLog.WriteError(exception, "Exception thrown in Test Job.");
}
}
This does not make the timer jobs robust enough, and there is a lot of boiler plate code. My question is how do I improve on this design? It's not the most elegant, I'm looking for a way to abstract the timer job operation code and have it's dependencies injected into it for each timer job. I would just like to hear your comments if you think this is a good approach. Or if someone has faced similar problems like this? Thanks
I think I've answered my own question with the presenter construction code above. When using dependency injection in a project, the injection itself is not that important, but the way it changes the way you write code is far more significant. I need to use a similar pattern such as command for my SharePoint timer job operations. I'd just like the bootstrapping to be handled better.

Best practice for SharePoint vanity url/redirection

My employer uses MOSS 2007 for our company intranet. It runs solely on secure http and is also exposed to the outside world via ISA. I currently have a request to add a forwarding URL to our site so that something like the following will occur:
intranet.mycompany.com/vanityname
redirects to ->
intranet.mycompany.com/somedeeplink/default.aspx
I fully expect this sort of thing will become more popular with our users as time goes on. So I am looking for a solution that scales. I have read various articles about creating a /site/ with forwarding meta tags or a forwarding SharePoint page type. I've also seen some that talk about adding virtual directories, etc directly in IIS. All these solutions seem to be overkill and inevitably take up more memory or processing time on the web servers.
I am currently leaning towards writing an http module that can be configured in the web.config and perform redirects. I wanted to get feedback to see if anyone else has done something similar in SharePoint 2007 and had any suggestions. Again, I'd like to implement something that scales without making major changes later on and is going to put minimal processing burden on our web servers. Thanks!
Ive implemented url redirecting with MOSS using the HTTP module route. I documented the code I used and what parameters worked the best for me here;
http://scaredpanda.com/2008/08/url-rewriting-with-sharepoint-moss-2007/
Take a look and let me know if this helps you and if you have any questions.
Update: The link above is no longer valid, so here text from the page that I used for URL redirect.
After messing around for a little bit it, I came up with a good way to do it. When I was looking for examples on the web there were a lot of people saying that it couldnt be done. But in the end it actually didn’t take much to implement it. Here’s an HttpModule that I wrote to do the work.
The key pieces are the this.app.BeginRequest += new EventHandler(app_BeginRequest) which
steps in front of the request and allows the module to get its redirect on.
And HttpContext.Current.RewritePath(redirect, false); will push the necessary headers n such forward so that the receiving .aspx page will understand how to correctly post back.
using System;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
using System.Collections;
using System.Text;
using System.Web;
using System.Web.Caching;
using System.Web.SessionState;
using System.Security.Cryptography;
using System.Configuration;
using System.Threading;
using System.IO;
using System.Security;
using System.Security.Principal;
namespace ScaredPanda
{
public sealed class RewriteHttpModule : IHttpModule
{
HttpApplication app = null;
///
/// Initializes the httpmodule
///
public void Init(HttpApplication httpapp)
{
this.app = httpapp;
this.app.BeginRequest += new EventHandler(app_BeginRequest);
}
public void app_BeginRequest(Object s, EventArgs e)
{
try
{
//determine if the income request is a url that we wish to rewrite.
//in this case we are looking for an extension-less request
string url = HttpContext.Current.Request.RawUrl.Trim();
if (url != string.Empty
&& url != "/"
&& !url.EndsWith("/pages")
&& !url.Contains(".aspx")
&& url.IndexOf("/", 1) == -1)
{
//this will build out the the new url that the user is redirected
//to ie pandas.aspx?pandaID=123
string redirect = ReturnRedirectUrl(url.Replace("/", ""));
//if you do a HttpContext.Current.RewritePath without the 'false' parameter,
//the receiving sharepoint page will not handle post backs correctly
//this is extremely useful in situations where users/admins will be doing a
//'site actions' event
HttpContext.Current.RewritePath(redirect, false);
}
}
catch (Exception ex)
{
//rubbish
}
}
}
}
Have you looked into Alternate Access Mappings?
http://technet.microsoft.com/en-us/library/cc263208.aspx
If you are open to paying for a solution. Have a look:
NOT FREE -> http://www.muhimbi.com/Products/SharePoint-URL-Shortener.aspx
Depending on the number of redirects you might want to implement an HTTP module as stated) but how about checking out
FREE -> http://www.codeplex.com/sharepointsmart404
Use the URL Rewrite feature in IIS. (I believe it's an IIS 7 extension)
http://www.iis.net/download/urlrewrite

Resources