|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
AbstractThis paper demonstrates creating a flexible, extensible, plug-in-based .NET Application (JOM - Smart Job Manager). JOM is an asynchronous job processing engine built using the MS Provider design pattern and the .NET technology available today. Technologies, libraries and patterns used here are .NET Framework 1.1, C#, MSMQ, Enterprise Library, MS Provider Model design pattern, .NET Windows Service and .NET Windows Application. IntroductionAsynchronous processing is desirable in many situations, for example, in a hospital the discharge summary needs to be printed for the patient along with email, FAX and SMS being sent to the Doctors, Clinicians notifying the discharge of the patient. All these tasks require processing time but you do not want the patient to be waiting for all these tasks to be completed. In this situation, you would implement an asynchronous process for FAX, EMAIL etc. so that you can concentrate on discharging the patient as quickly as possible. Smart Job Manager Framework is designed with this asynchronous scenario in mind. Smart Job Manager (JOM) runs as a Windows Service and the client applications will queue the jobs to Microsoft Message Queuing (MSMQ). JOM is developed using the MS Provider Design Pattern which gives the framework flexibility, extensibility and plug ability on the fly. JOM receives the job from the queue and allocates a proper Provider to accomplish it asynchronously. Introducing the patterns and technologies used in Smart Job ManagerIf you are already familiar with the MS Provider Design Pattern, MSMQ and Enterprise Library you can skip this section. MS Provider design patternMS Provider design pattern helps to abstract the application from specific provider dependency and makes the application extensible and flexible. An abstract base class holds properties and methods required to be implemented in the concrete provider API. Also the description of the provider is defined in the configuration file, which allows the plug-in implementation of the API. Have a look at the ASP.NET Provider Toolkit where Rob Howard describes about the provider model and its specifications. MSMQJOM uses MSMQ to receive a task and adds it to the queue. Message queuing provides a kind of store-and-forward messaging in a pre-built infrastructure that helps to address the asynchronous scenarios. Message queuing provides guaranteed message delivery, efficient routing, security, and priority-based messaging. The .NET Framework provides access to all message queuing operation out of the box via the System.Messaging.dll. Enterprise libraryThe current buzz in the .NET community surrounds the Enterprise Library from Patterns and Practices Group, which ships with seven reusable application blocks caching, configuration, cryptography, data access, exception handling, logging and instrumentation, and security. The aim of the application blocks is to reduce development cost and increase confidence. It took me only couple of hours to add functionalities like caching, exception handling and logging features into JOM and for this the June 2005 release of the application blocks was used. These application blocks are great tools that enable us to maintain consistency and efficiency in our applications. Using the application blocks makes our programs easier to write as it follows similar patterns throughout and the common tasks are encapsulated into easy to use classes and static methods. Smart Job ManagerJOM facilitates communication between two or more dissimilar systems that are not in sync. JOM runs as a Windows Service, receiving jobs from MSMQ and dynamically assigning providers to accomplish the jobs.
Fig. Smart Job Manager workflow To understand the JOM Framework we need to understand the role of the Job object in the JOM Framework
Fig. Business object: A client application simply needs to create a Job job = new Job(0,0,DateTime.Now,
DateTime.Now,"SimpleJobManagerProvider");
JobDetailCollection jobDetails = new JobDetailCollection();
JobDetail jobDetail = new JobDetail(0,0,
"Copyright","Use at your own risk.");
jobDetails.Add(jobDetail);
jobDetail = new JobDetail(0,0,"PersonName","Shahed Khan");
jobDetails.Add(jobDetail);
jobDetail = new JobDetail(1,0,"Email","shahed.khan@gmail.com");
jobDetails.Add(jobDetail);
job.JobDetailCollection = jobDetails;
QueueJob(job, QueuePath);
In the above code, we simply created a An implementation of the provider pattern in JOMAs JOM implements the provider pattern it is extensible, flexible and provides a plug-in based implementation of the provider APIs. The provider pattern is used here to overcome the limitations of a predetermined provider API and predetermined functionalities. Instead, it is designed to adopt any provider API that implements the Let's walk through a simple example demonstrating how this pattern has been implemented in JOM.
Fig Smart Job Manager and the provider pattern public abstract class JobManagerProvider : ProviderBase {
public abstract bool DoJob(Job job);
}
The public class SimpleJobManagerProvider : JobManagerProvider {
public override bool DoJob(Job job){
//DoJob
//Write to file
StreamWriter sw = new StreamWriter(string.Format("{0}{1}{2}{3}",
OutputFilePath, "SimpleJob",
DateTime.Now.Ticks.ToString(), ".txt"));
sw.AutoFlush = true; // good practice if you've lots of data
sw.WriteLine(job.JobManagerProviderName);
foreach(JobDetail jobDetail in job.JobDetailCollection){
sw.WriteLine(string.Format("{0}: {1}",
jobDetail.ElementDesc, jobDetail.ElementData));
}
sw.Close();
return false;
}
}
In the above code, the JOM decides on dynamically loading the provider API based on the provider name passed to it via the private void OnMessageArrival(IAsyncResult ar) {
MessageQueue mq = (MessageQueue)ar.AsyncState;
Job job = new Job();
try{
Message msg=mq.EndReceive(ar);
job = (Job) msg.Body;
JobManager.DoJob(job);
}
catch(Exception e){
EntLibHelper.JobException("OnMessageArrival",job,e);
}
finally{
mq.BeginReceive(TimeSpan.FromSeconds(5),mq,
new AsyncCallback(OnMessageArrival));
}
}
The above code shows the public class JobManager {
public static bool DoJob (Job job){
JobManagerProvider provider =
JobManagerProvider.Instance(job.JobManagerProviderName);
return provider.DoJob(job);
}
}
In the above code we see that the Configuration information: As described in the MS Provider specification after the concrete provider is implemented, it must be described in the configuration section. The beauty of the provider pattern is that the appropriate provider is instantiated at run time from the information that is contained in the configuration file and you can define an unlimited number of providers. <smartJobManager.providers>
<jobManager defaultProvider="SimpleJobManagerProvider">
<providers>
<add name = "SimpleJobManagerProvider"
type = "SmartJobManager.SimpleJobManagerProvider,
SimpleJobManagerProvider"/>
</providers>
</jobManager>
</smartJobManager.providers>
In the above section, we have added the For a new provider to be implemented, we simply need to write a new provider extending from Use of Enterprise Library in the JOM FrameworkJOM uses Enterprise Library June 2005 for caching, logging and exception handling. This facilitates the JOM to easily transform these features according to your needs. The following code block demonstrates how caching, logging and exception handling is implemented in JOM. Caching of providers in JOMProviders are added to the cache as the reflection used later is expensive: // Use the cache because the reflection used later is expensive
Type type = null;
string cacheKey = null;
// Get the names of the providers
JobManagerConfiguration config =
JobManagerConfiguration.GetConfig();
// Read the configuration specific information for this provider
Provider jobManagerProvider =
(Provider) config.Providers[providerName];
// In the cache?
cacheKey = "Job::" + providerName;
if (EntLibHelper.GetCachedObject(cacheKey) == null){
// The assembly should be in \bin or GAC, so we simply need
// to get an instance of the type
try {
type = Type.GetType( jobManagerProvider.Type );
// Insert the type into the cache
Type[] paramTypes = System.Type.EmptyTypes;
EntLibHelper.StoreInCache( cacheKey,
type.GetConstructor(paramTypes) );
}
catch (Exception e) {
throw new Exception("Unable to load provider", e);
}
}
// Load the configuration settings
object[] paramArray = new object[0];
return (JobManagerProvider)(((ConstructorInfo)
EntLibHelper.GetCachedObject(cacheKey)).Invoke(paramArray) );
Logging in JOMFor logging, simple flat file sink that ships with Enterprise Library is used. You can transfer easily to a DB sink or any of your customized sink: try{
Message msg=mq.EndReceive(ar);
job = (Job) msg.Body;
if (job != null){
//Log Recieve of a Job
EntLibHelper.JobInfo("Recieved New Job",job);
if (!JobManager.DoJob(job)){
//Log Error
EntLibHelper.JobError("JobError Occured",job);
}
}
}
catch(MessageQueueException e) {
if (e.MessageQueueErrorCode !=
MessageQueueErrorCode.IOTimeout){
//Log Error
EntLibHelper.Exception("OnMessageArrival",e);
}
}
Exception handling in JOMAll exceptions are logged using a flat file sink. You can modify according to your need: catch(Exception e){
EntLibHelper.JobException("OnMessageArrival",job,e);
}
Enterprise Library configuration
Fig Enterprise Library configuration screen How to use Smart Job Manager in your own applicationJOM stepsImplement a concrete provider inherited from Decide what you will be passing via the In your provider, include a Client application stepsPrepare a Comparing with "Distributed system design with command pattern"Brad King in his article Simplify Distributed System Design Using the Command Pattern, MSMQ, and .NET takes a different approach by passing a command object to the queue:
Fig Distributed system with command pattern workflow The good thing about this approach is it’s strongly typed and it can apply validations on business objects. But the limitation is both the client and the invoker need to know the original definition of every command object. So all the command objects should be residing in both the client app and the invoker app. However in JOM we used a common business object
Fig. Smart Job Manager workflow Future enhancementsHere in the demo application simple recoverable messages are sent to MSMQ. However you might want to implement reliable messages, create transactional queues, and implement acknowledgement, timeout and journaling on the messages. Also this application can be designed so that you can manage the configuration from the Enterprise Library UI. The code snippet demonstrating how the private static void QueueJob(Job job, string destinationQueue){
try{
// open the queue
MessageQueue mq = new MessageQueue(destinationQueue);
// set the message to durable.
mq.DefaultPropertiesToSend.Recoverable = true;
// set the formatter to Binary, default is XML
mq.Formatter = new BinaryMessageFormatter();
// send the job object
mq.Send(job, "Job Message Test");
mq.Close();
}
catch (Exception e){
Console.WriteLine(e.ToString());
}
}
This code reads messages from the queue: try{
Message msg=mq.EndReceive(ar);
job = (Job) msg.Body;
JobManager.DoJob(job);
}
Demo appA full demonstration of the JOM workflow is provided in the demo application. Demo includes a number of concrete providers implemented from Providers
JOM Windows serviceJOM Windows service hosts all the providers mentioned above. If you want to host your own providers with JOM simply follow the instructions discussed earlier. JOM seeks new jobs in the MSMQ every 5 sec.
Fig. View and start the .NET Job Manager Service with computer management tool
Fig. View queued jobs with the computer management tool Client applicationThe client application has the interface to send jobs to the queue. This can create jobs for all the providers mentioned above:
Fig. Smart Job Manager workflow Checklist to run the demo applicationView the Readme.txt file provided with demo. ConclusionSmart Job Manager is architected using provider pattern which makes it flexible, pluggable and extensible. Also the use of Special Thanks to Christopher Heale for proof reading.
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||