Sedan har vi själva ControllerServices alltså den klass som ärver från vår baseServices.
/// <summary>
/// Controller services
/// </summary>
/// <remarks>
/// <para>
/// The controller services that handles incomming messages for the controller
/// </para>
/// </remarks>
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, Name = "controllerservices", Namespace = "http://services.ie.gladh.org/")]
public class ControllerServices : baseServices, IControllerServices
{
#region -- Declaration
private event CommandProcessedEventHandler _CommandProcessedEvent;
private event ProcessCommandEventHandler _ProcessCommandEvent;
#endregion
#region -- Override methods
protected override void IncomingMessageDequeueFromQueue(object message)
{
_Log.TraceEnter();
try
{
if(message.GetType() == typeof(CommandProcessedMessage))
if (_CommandProcessedEvent != null)
_CommandProcessedEvent(new CommandProcessedEventArgs(message as CommandProcessedMessage));
if (message.GetType() == typeof(ProcessCommandMessage))
if (_ProcessCommandEvent != null)
_ProcessCommandEvent(new ProcessCommandEventArgs(message as ProcessCommandMessage));
}
catch (Exception exception)
{
_Log.Error<object>("An error occured trying to raise event that a messages has been read from queue", exception, message);
OnErrorOccuredEvent(new ErrorOccuredEventArgs(string.Format("An error occured trying to raise event that a messages has been read from queue: {0}", exception.Message)));
}
finally
{
_Log.TraceLeave();
}
}
#endregion
#region -- Constructor
public ControllerServices() : base(typeof(ControllerServices)) { }
#endregion
#region IControllerServices Members
event ProcessCommandEventHandler IControllerServices.ProcessCommandEvent
{
add { _ProcessCommandEvent += value; }
remove { _ProcessCommandEvent -= value; }
}
event CommandProcessedEventHandler IControllerServices.CommandProcessedEvent
{
add { _CommandProcessedEvent += value; }
remove { _CommandProcessedEvent -= value; }
}
event ErrorOccuredEventHandler IControllerServices.ErrorOccuredEvent
{
add { base._ErrorOccuredEvent += value; }
remove { base._ErrorOccuredEvent -= value; }
}
void IControllerServices.ProcessCommand(ProcessCommandMessage processCommandMessage)
{
base.AddIncomingMessageToQueue(processCommandMessage);
}
void IControllerServices.CommandProcessed(CommandProcessedMessage commandProcessedMessage)
{
base.AddIncomingMessageToQueue(commandProcessedMessage);
}
#endregion
}
Här ser vi de två WCF interfacen som våra klienter använder sig av när de vill anropa till servern. IControllerServices.ProcessCommand och IControllerServices.CommandProcessed så när klienten anropa servern på någon av dessa metoder, så kommer vi ta meddelandet och lägga ner det i kön, och sedan får en annan tråd ta hand om själva uppläsningen och ytterligare en annan tråd tar hand om exekveringen av meddelandet.
Metoden IncomingMessageDequeueFromQueue är den metod som blir anropad när ett meddelande har blivit läst från kön och skall exekveras vidare av vår server, så vår controllerServices reser ett event till Controllern att ett meddelande har kommit in och Controllern tar nu hand om det med hjälp av följande metoder:
private void ControllerServices_ProcessCommandEvent(ProcessCommandEventArgs processCommandEventArgs)
{
ProcessCommandMessage processCommandMessage = processCommandEventArgs.ProcessCommandMessage;
this.GetType().InvokeMember(processCommandMessage.ProcessCommand.ToString()
, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod
, null, this, new object[] { processCommandMessage });
}
private void ControllerServices_CommandProcessedEvent(CommandProcessedEventArgs commandProcessedEventArgs)
{
CommandProcessedMessage commandProcessedMessage = commandProcessedEventArgs.CommandProcessedMessage;
this.GetType().InvokeMember(commandProcessedMessage.CommandProcessed.ToString()
, System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.InvokeMethod
, null, this, new object[] { commandProcessedMessage });
}
Anledningen till att jag använder mig av InvokeMember() som är lite långsammar än att köra en stor switch-sats där man kontrollerar vilken commandProcessed det är och sedan anropar metoden, är att jag vill ha mindre kod att underhålla, om jag lägger till ett nytt CommandProcessed så slipper jag underhålla koden i dessa metoder, och skall "bara" kommaihåg att skriva den kod som skall processas för den nya CommandProcessed typen.
Så säg att jag skickar ett meddelande om att att en av mina Executer har startat, då vill jag att min Controller skall veta det. Executern kallar på controller.CommandProcessed(ExecuterStarted). ControllerServices tar emot meddelandet och lägger ner det på kö, en anna tråd läser meddelandet från kön, och startar en ny tråd som reser ett event om att ett commandProcessed meddelande har inkommit. Metoden ControllerServices_CommandProcessedEvent ser att det är av typen ExecuterStarted och anropar därför metoden ExecuterStarted som finns i min Controller-klass, som utför det som skall utföras när en Executer har startat.
Och så här ser metoden ut (i controllern) som körs när en Executer har startas
private void ExecuterStarted(CommandProcessedMessage commandProcessedMessage)
{
_Log.TraceEnter();
try
{
ExecuterDataEntity executerData = commandProcessedMessage.Body.ExecuterData;
_Log.Information<CommandProcessedMessage>(
string.Format("Executer Id:{0} Uri:{1} has started"
, executerData.FriendlyName, executerData.EndpointAdressURI), commandProcessedMessage);
//-- Change the status for this executer to started
ExecuterDataEntityWrapper executerDataEntityWrapper = FindExecuterInList(executerData.Identifier);
lock (executerDataEntityWrapper.SyncLock)
executerDataEntityWrapper.ExecuterData.Status = ExecuterStatusEnum.Started;
//-- Add this executer to the activeexecuter list so it can host and run adapters.
lock (_ActiveExecuterList.SyncLock)
_ActiveExecuterList.Add(executerDataEntityWrapper);
//-- Send CommandProcessed to frontend.
SendCommandProcessedMessage(commandProcessedMessage);
}
finally
{
_Log.TraceLeave();
}
}
Som du märker i den sista metoden så har jag lite Lock()'s och det är ju viktigt för att jag skall accessa variabler som är delade mellan alla trådar som körs, och då vill jag vara säker på att 2 trådar inte kommer in samtidigt och skriver till variablen, det leder bara till problem....
Där har du en snabb genomgång av hur det är tänkt med köerna som skall hantera inkommande meddeland.