Skip to content

Commit e512568

Browse files
introfogiText-CI
authored andcommitted
Add event repeat logic
DEVSIX-5973 Autoported commit. Original commit hash: [a97fd89b3]
1 parent ecb340d commit e512568

File tree

4 files changed

+146
-22
lines changed

4 files changed

+146
-22
lines changed

itext.tests/itext.commons.tests/itext/commons/actions/ProductEventHandlerTest.cs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2020
You should have received a copy of the GNU Affero General Public License
2121
along with this program. If not, see <https://www.gnu.org/licenses/>.
2222
*/
23+
using System;
2324
using iText.Commons.Actions.Confirmations;
25+
using iText.Commons.Actions.Processors;
2426
using iText.Commons.Actions.Sequence;
2527
using iText.Commons.Ecosystem;
2628
using iText.Commons.Exceptions;
@@ -29,14 +31,20 @@ You should have received a copy of the GNU Affero General Public License
2931

3032
namespace iText.Commons.Actions {
3133
public class ProductEventHandlerTest : ExtendedITextTest {
34+
[NUnit.Framework.SetUp]
35+
public virtual void ClearProcessors() {
36+
ProductEventHandler.INSTANCE.ClearProcessors();
37+
}
38+
3239
[NUnit.Framework.Test]
3340
public virtual void UnknownProductTest() {
3441
ProductEventHandler handler = ProductEventHandler.INSTANCE;
35-
NUnit.Framework.Assert.That(() => {
36-
handler.OnAcceptedEvent(new ITextTestEvent(new SequenceId(), null, "test-event", "Unknown Product"));
37-
}
38-
, NUnit.Framework.Throws.InstanceOf<UnknownProductException>().With.Message.EqualTo(MessageFormatUtil.Format(UnknownProductException.UNKNOWN_PRODUCT, "Unknown Product")))
39-
;
42+
AbstractContextBasedITextEvent @event = new ITextTestEvent(new SequenceId(), null, "test-event", "Unknown Product"
43+
);
44+
Exception ex = NUnit.Framework.Assert.Catch(typeof(UnknownProductException), () => handler.OnAcceptedEvent
45+
(@event));
46+
NUnit.Framework.Assert.AreEqual(MessageFormatUtil.Format(UnknownProductException.UNKNOWN_PRODUCT, "Unknown Product"
47+
), ex.Message);
4048
}
4149

4250
[NUnit.Framework.Test]
@@ -83,5 +91,63 @@ public virtual void ConfirmEventTest() {
8391
NUnit.Framework.Assert.AreEqual(@event, ((ConfirmedEventWrapper)handler.GetEvents(sequenceId)[0]).GetEvent
8492
());
8593
}
94+
95+
[NUnit.Framework.Test]
96+
public virtual void RepeatEventHandlingWithFiveExceptionOnProcessingTest() {
97+
ProductEventHandler handler = ProductEventHandler.INSTANCE;
98+
handler.AddProcessor(new ProductEventHandlerTest.RepeatEventProcessor(5));
99+
AbstractContextBasedITextEvent @event = new ITextTestEvent(new SequenceId(), null, "test", ProductNameConstant
100+
.ITEXT_CORE);
101+
Exception e = NUnit.Framework.Assert.Catch(typeof(ProductEventHandlerRepeatException), () => handler.OnAcceptedEvent
102+
(@event));
103+
NUnit.Framework.Assert.AreEqual("customMessage5", e.Message);
104+
}
105+
106+
[NUnit.Framework.Test]
107+
public virtual void RepeatEventHandlingWithFourExceptionOnProcessingTest() {
108+
ProductEventHandler handler = ProductEventHandler.INSTANCE;
109+
handler.AddProcessor(new ProductEventHandlerTest.RepeatEventProcessor(4));
110+
AbstractContextBasedITextEvent @event = new ITextTestEvent(new SequenceId(), null, "test", ProductNameConstant
111+
.ITEXT_CORE);
112+
NUnit.Framework.Assert.DoesNotThrow(() => handler.OnAcceptedEvent(@event));
113+
}
114+
115+
[NUnit.Framework.Test]
116+
public virtual void RepeatEventHandlingWithOneExceptionOnProcessingTest() {
117+
ProductEventHandler handler = ProductEventHandler.INSTANCE;
118+
handler.AddProcessor(new ProductEventHandlerTest.RepeatEventProcessor(1));
119+
AbstractContextBasedITextEvent @event = new ITextTestEvent(new SequenceId(), null, "test", ProductNameConstant
120+
.ITEXT_CORE);
121+
NUnit.Framework.Assert.DoesNotThrow(() => handler.OnAcceptedEvent(@event));
122+
}
123+
124+
private class RepeatEventProcessor : ITextProductEventProcessor {
125+
private readonly int exceptionsCount;
126+
127+
private int exceptionCounter = 0;
128+
129+
public RepeatEventProcessor(int exceptionsCount) {
130+
this.exceptionsCount = exceptionsCount;
131+
}
132+
133+
public virtual void OnEvent(AbstractProductProcessITextEvent @event) {
134+
if (exceptionCounter < exceptionsCount) {
135+
exceptionCounter++;
136+
throw new ProductEventHandlerRepeatException("customMessage" + exceptionCounter);
137+
}
138+
}
139+
140+
public virtual String GetProductName() {
141+
return ProductNameConstant.ITEXT_CORE;
142+
}
143+
144+
public virtual String GetUsageType() {
145+
return "someUsage";
146+
}
147+
148+
public virtual String GetProducer() {
149+
return "someProducer";
150+
}
151+
}
86152
}
87153
}

itext/itext.commons/itext/commons/actions/ProductEventHandler.cs

Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ internal sealed class ProductEventHandler : AbstractContextBasedEventHandler {
4343
private static readonly ILogger LOGGER = ITextLogManager.GetLogger(typeof(iText.Commons.Actions.ProductEventHandler
4444
));
4545

46+
// The constant has the following value for two reasons. First, to avoid the infinite loop.
47+
// Second, to retry event processing several times for technical reasons.
48+
private const int MAX_EVENT_RETRY_COUNT = 4;
49+
4650
private readonly ConcurrentDictionary<String, ITextProductEventProcessor> processors = new ConcurrentDictionary
4751
<String, ITextProductEventProcessor>();
4852

@@ -59,25 +63,18 @@ private ProductEventHandler()
5963
/// </summary>
6064
/// <param name="event">to handle</param>
6165
protected internal override void OnAcceptedEvent(AbstractContextBasedITextEvent @event) {
62-
if (!(@event is AbstractProductProcessITextEvent)) {
63-
return;
64-
}
65-
AbstractProductProcessITextEvent productEvent = (AbstractProductProcessITextEvent)@event;
66-
String productName = productEvent.GetProductName();
67-
ITextProductEventProcessor productEventProcessor = GetActiveProcessor(productName);
68-
if (productEventProcessor == null) {
69-
throw new UnknownProductException(MessageFormatUtil.Format(UnknownProductException.UNKNOWN_PRODUCT, productName
70-
));
71-
}
72-
productEventProcessor.OnEvent(productEvent);
73-
if (productEvent.GetSequenceId() != null) {
74-
if (productEvent is ConfirmEvent) {
75-
WrapConfirmedEvent((ConfirmEvent)productEvent, productEventProcessor);
66+
for (int i = 0; i < MAX_EVENT_RETRY_COUNT; i++) {
67+
try {
68+
TryProcessEvent(@event);
69+
// process succeeded
70+
return;
7671
}
77-
else {
78-
AddEvent(productEvent.GetSequenceId(), productEvent);
72+
catch (ProductEventHandlerRepeatException) {
7973
}
8074
}
75+
// ignore this exception to retry the processing
76+
// the final processing retry
77+
TryProcessEvent(@event);
8178
}
8279

8380
internal ITextProductEventProcessor AddProcessor(ITextProductEventProcessor processor) {
@@ -133,6 +130,28 @@ internal void AddEvent(SequenceId id, AbstractProductProcessITextEvent @event) {
133130
}
134131
}
135132

133+
private void TryProcessEvent(AbstractContextBasedITextEvent @event) {
134+
if (!(@event is AbstractProductProcessITextEvent)) {
135+
return;
136+
}
137+
AbstractProductProcessITextEvent productEvent = (AbstractProductProcessITextEvent)@event;
138+
String productName = productEvent.GetProductName();
139+
ITextProductEventProcessor productEventProcessor = GetActiveProcessor(productName);
140+
if (productEventProcessor == null) {
141+
throw new UnknownProductException(MessageFormatUtil.Format(UnknownProductException.UNKNOWN_PRODUCT, productName
142+
));
143+
}
144+
productEventProcessor.OnEvent(productEvent);
145+
if (productEvent.GetSequenceId() != null) {
146+
if (productEvent is ConfirmEvent) {
147+
WrapConfirmedEvent((ConfirmEvent)productEvent, productEventProcessor);
148+
}
149+
else {
150+
AddEvent(productEvent.GetSequenceId(), productEvent);
151+
}
152+
}
153+
}
154+
136155
private void WrapConfirmedEvent(ConfirmEvent @event, ITextProductEventProcessor productEventProcessor) {
137156
lock (events) {
138157
IList<AbstractProductProcessITextEvent> eventsList = events.Get(@event.GetSequenceId());
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
This file is part of the iText (R) project.
3+
Copyright (c) 1998-2021 iText Group NV
4+
Authors: iText Software.
5+
6+
This program is offered under a commercial and under the AGPL license.
7+
For commercial licensing, contact us at https://itextpdf.com/sales. For AGPL licensing, see below.
8+
9+
AGPL licensing:
10+
This program is free software: you can redistribute it and/or modify
11+
it under the terms of the GNU Affero General Public License as published by
12+
the Free Software Foundation, either version 3 of the License, or
13+
(at your option) any later version.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU Affero General Public License for more details.
19+
20+
You should have received a copy of the GNU Affero General Public License
21+
along with this program. If not, see <https://www.gnu.org/licenses/>.
22+
*/
23+
using System;
24+
25+
namespace iText.Commons.Exceptions {
26+
/// <summary>The class represents a signal to the event handler that it is necessary to repeat the handling of the current event.
27+
/// </summary>
28+
public sealed class ProductEventHandlerRepeatException : ITextException {
29+
/// <summary>
30+
/// Creates a new instance of
31+
/// <see cref="ProductEventHandlerRepeatException"/>
32+
/// based on message.
33+
/// </summary>
34+
/// <param name="message">the detail message</param>
35+
public ProductEventHandlerRepeatException(String message)
36+
: base(message) {
37+
}
38+
}
39+
}

port-hash

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
d5ce39e969e758cd6af88bbe43910a9a0e228803
1+
a97fd89b33c3ba64728f63c93632a56301f63e53

0 commit comments

Comments
 (0)