-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Try-catch clause #525
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Try-catch clause #525
Changes from all commits
e8000c3
61dfc85
c49a07b
718cb69
4e94b89
01d9666
3a2fa0d
338a676
1a213d3
ddfa996
edf616e
efdaf17
94a8a71
fe75379
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using System; | ||
|
||
namespace WorkflowCore.Interface | ||
{ | ||
public interface ICatchStepBuilder<TData, TStepBody> : IStepBuilder<TData, TStepBody>, ITryStepBuilder<TData, TStepBody> | ||
where TStepBody : IStepBody | ||
{ | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using WorkflowCore.Models; | ||
|
||
namespace WorkflowCore.Interface | ||
{ | ||
public interface ITryStepBuilder<TData, TStepBody> | ||
where TStepBody : IStepBody | ||
{ | ||
ICatchStepBuilder<TData, TStepBody> Catch<TStep>(IEnumerable<Type> exceptionTypes, Action<IStepBuilder<TData, TStep>> stepSetup = null) | ||
where TStep : IStepBody; | ||
|
||
ICatchStepBuilder<TData, TStepBody> Catch(IEnumerable<Type> exceptionTypes, Action<IStepExecutionContext> body); | ||
|
||
ICatchStepBuilder<TData, TStepBody> Catch(IEnumerable<Type> exceptionTypes, | ||
Func<IStepExecutionContext, ExecutionResult> body); | ||
|
||
ICatchStepBuilder<TData, TStepBody> CatchWithSequence(IEnumerable<Type> exceptionTypes, | ||
Action<IWorkflowBuilder<TData>> builder); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
using WorkflowCore.Models; | ||
|
||
namespace WorkflowCore.Interface | ||
{ | ||
public interface IWorkflowDefinitionValidator | ||
{ | ||
bool IsDefinitionValid(WorkflowDefinition definition); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this part of the try/catch feature? or something else? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right now this only checks that every |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,9 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Text; | ||
|
||
namespace WorkflowCore.Models.LifeCycleEvents | ||
{ | ||
public class WorkflowTerminated : LifeCycleEvent | ||
{ | ||
public SerializableException Exception { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
using System; | ||
|
||
namespace WorkflowCore.Models | ||
{ | ||
public class SerializableException | ||
{ | ||
public string FullTypeName { get; private set; } | ||
|
||
public string Message { get; private set; } | ||
|
||
public string StackTrace { get; private set; } | ||
|
||
public SerializableException(Exception exception) | ||
{ | ||
FullTypeName = exception.GetType().FullName; | ||
Message = exception.Message; | ||
StackTrace = exception.StackTrace; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using WorkflowCore.Interface; | ||
using WorkflowCore.Models; | ||
using WorkflowCore.Models.LifeCycleEvents; | ||
|
||
namespace WorkflowCore.Services.ErrorHandlers | ||
{ | ||
public class CatchHandler : IWorkflowErrorHandler | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you implement a set of unit tests for this class that make it's behavior explicit? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure, will do it soon. By the way I have a question concerning behaviour of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe that was for bubbling up the call stack of nested containers There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I clearly have troubles understanding this part of bubbling up the call stack. Could you please go into deeper details? And please tell me if it's needed or not in case of |
||
{ | ||
private readonly ILifeCycleEventPublisher _eventPublisher; | ||
private readonly IExecutionPointerFactory _pointerFactory; | ||
private readonly IDateTimeProvider _datetimeProvider; | ||
private readonly WorkflowOptions _options; | ||
|
||
public CatchHandler(IExecutionPointerFactory pointerFactory, ILifeCycleEventPublisher eventPublisher, IDateTimeProvider datetimeProvider, WorkflowOptions options) | ||
{ | ||
_pointerFactory = pointerFactory; | ||
_eventPublisher = eventPublisher; | ||
_datetimeProvider = datetimeProvider; | ||
_options = options; | ||
} | ||
|
||
public WorkflowErrorHandling Type => WorkflowErrorHandling.Catch; | ||
|
||
public void Handle(WorkflowInstance workflow, WorkflowDefinition def, ExecutionPointer exceptionPointer, WorkflowStep step, | ||
Exception exception, Queue<ExecutionPointer> bubbleUpQueue) | ||
{ | ||
var scope = new Stack<string>(exceptionPointer.Scope.Reverse()); | ||
scope.Push(exceptionPointer.Id); | ||
|
||
var exceptionCaught = false; | ||
|
||
while (scope.Any()) | ||
{ | ||
var pointerId = scope.Pop(); | ||
var scopePointer = workflow.ExecutionPointers.FindById(pointerId); | ||
var scopeStep = def.Steps.FindById(scopePointer.StepId); | ||
|
||
if ((scopeStep.ErrorBehavior ?? WorkflowErrorHandling.Catch) != WorkflowErrorHandling.Catch) | ||
{ | ||
bubbleUpQueue.Enqueue(scopePointer); | ||
continue; | ||
} //TODO: research if it's needed | ||
|
||
scopePointer.Active = false; | ||
scopePointer.EndTime = _datetimeProvider.Now.ToUniversalTime(); | ||
scopePointer.Status = PointerStatus.Failed; | ||
|
||
while (scopeStep.CatchStepsQueue.Count != 0) | ||
{ | ||
var nextCatchStepPair = scopeStep.CatchStepsQueue.Dequeue(); | ||
var exceptionType = nextCatchStepPair.Key; | ||
var catchStepId = nextCatchStepPair.Value; | ||
if (exceptionType.IsInstanceOfType(exception)) | ||
{ | ||
var catchPointer = _pointerFactory.BuildCatchPointer(def, scopePointer, exceptionPointer, catchStepId, exception); | ||
workflow.ExecutionPointers.Add(catchPointer); | ||
|
||
foreach (var outcomeTarget in scopeStep.Outcomes.Where(x => x.Matches(workflow.Data))) | ||
workflow.ExecutionPointers.Add(_pointerFactory.BuildNextPointer(def, scopePointer, outcomeTarget)); | ||
|
||
exceptionCaught = true; | ||
|
||
scopeStep.CatchStepsQueue.Clear(); | ||
scope.Clear(); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
if (!exceptionCaught) | ||
{ | ||
workflow.Status = WorkflowStatus.Terminated; | ||
_eventPublisher.PublishNotification(new WorkflowTerminated() | ||
{ | ||
EventTimeUtc = _datetimeProvider.UtcNow, | ||
Reference = workflow.Reference, | ||
WorkflowInstanceId = workflow.Id, | ||
WorkflowDefinitionId = workflow.WorkflowDefinitionId, | ||
Version = workflow.Version, | ||
Exception = new SerializableException(exception) | ||
}); | ||
} | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need an empty interface?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This interface gives a builder full functionality of
IStepBuilder
and also allows toCatch()
if we want to have multiple catches after oneTry()
. It is used as a return type for builder after the firstCatch()
, whereas afterTry()
we want to allow only toCatch()
.