Skip to content

Commit c9acaa0

Browse files
committed
DataHandlerCounter disabling hooks mechanism
DEVSIX-5183 Autoported commit. Original commit hash: [33c3b6eae] Manual files: kernel/src/main/java/com/itextpdf/kernel/counter/data/EventDataHandlerUtil.java
1 parent 9d09a57 commit c9acaa0

File tree

6 files changed

+360
-29
lines changed

6 files changed

+360
-29
lines changed
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
using System;
2+
using System.Threading;
3+
using iText.IO.Util;
4+
using iText.Kernel;
5+
using iText.Kernel.Counter.Data;
6+
using iText.Kernel.Counter.Event;
7+
using iText.Test;
8+
using iText.Test.Attributes;
9+
10+
namespace iText.Kernel.Counter {
11+
public class DataHandlerCounterTest : ExtendedITextTest {
12+
[NUnit.Framework.Test]
13+
public virtual void DisableHooksTest() {
14+
int betweenChecksSleepTime = 500;
15+
int handlerSleepTime = 100;
16+
DataHandlerCounterTest.TestDataHandler dataHandler = new DataHandlerCounterTest.TestDataHandler(handlerSleepTime
17+
);
18+
DataHandlerCounterTest.TestDataHandlerCounter counter = new DataHandlerCounterTest.TestDataHandlerCounter(
19+
dataHandler);
20+
// check the initial process count
21+
NUnit.Framework.Assert.AreEqual(0, dataHandler.GetProcessCount());
22+
long count = dataHandler.GetProcessCount();
23+
Thread.Sleep(betweenChecksSleepTime);
24+
// check that process count has been updated
25+
NUnit.Framework.Assert.AreNotEqual(count, dataHandler.GetProcessCount());
26+
counter.Close();
27+
// ensure that last process on disable would be finished
28+
Thread.Sleep(betweenChecksSleepTime);
29+
long totalCount = dataHandler.GetProcessCount();
30+
Thread.Sleep(betweenChecksSleepTime);
31+
// ensure that after disabling there are no new processes has been invoked
32+
NUnit.Framework.Assert.AreEqual(totalCount, dataHandler.GetProcessCount());
33+
}
34+
35+
[NUnit.Framework.Test]
36+
public virtual void OnEventAfterDisableTest() {
37+
DataHandlerCounterTest.TestDataHandlerCounter counter = new DataHandlerCounterTest.TestDataHandlerCounter(
38+
new DataHandlerCounterTest.TestDataHandler(100));
39+
DataHandlerCounterTest.TestEvent testEvent = new DataHandlerCounterTest.TestEvent("test");
40+
NUnit.Framework.Assert.DoesNotThrow(() => counter.OnEvent(testEvent, null));
41+
counter.Close();
42+
NUnit.Framework.Assert.That(() => {
43+
counter.OnEvent(testEvent, null);
44+
}
45+
, NUnit.Framework.Throws.InstanceOf<InvalidOperationException>().With.Message.EqualTo(PdfException.DataHandlerCounterHasBeenDisabled))
46+
;
47+
}
48+
49+
[NUnit.Framework.Test]
50+
public virtual void MultipleRegisterHooksTest() {
51+
DataHandlerCounterTest.TestDataHandler dataHandler = new DataHandlerCounterTest.TestDataHandler(200);
52+
DataHandlerCounterTest.TestDataHandlerCounter counter = new DataHandlerCounterTest.TestDataHandlerCounter(
53+
dataHandler);
54+
DataHandlerCounterTest.TestDataHandlerCounter secondCounter = new DataHandlerCounterTest.TestDataHandlerCounter
55+
(dataHandler);
56+
NUnit.Framework.Assert.DoesNotThrow(() => counter.Close());
57+
NUnit.Framework.Assert.DoesNotThrow(() => secondCounter.Close());
58+
}
59+
60+
[NUnit.Framework.Test]
61+
// count set explicitly as it is required that the test should log this message only once
62+
[LogMessage(iText.IO.LogMessageConstant.UNEXPECTED_EVENT_HANDLER_SERVICE_THREAD_EXCEPTION, Count = 1, LogLevel
63+
= LogLevelConstants.ERROR)]
64+
public virtual void TimedProcessWithExceptionTest() {
65+
int betweenChecksSleepTime = 500;
66+
int handlerSleepTime = 100;
67+
DataHandlerCounterTest.TestDataHandlerWithException dataHandler = new DataHandlerCounterTest.TestDataHandlerWithException
68+
(handlerSleepTime);
69+
DataHandlerCounterTest.TestDataHandlerCounter counter = new DataHandlerCounterTest.TestDataHandlerCounter(
70+
dataHandler);
71+
// check the initial process count
72+
NUnit.Framework.Assert.AreEqual(0, dataHandler.GetProcessCount());
73+
Thread.Sleep(betweenChecksSleepTime);
74+
// check that process count has not been updated
75+
NUnit.Framework.Assert.AreEqual(0, dataHandler.GetProcessCount());
76+
NUnit.Framework.Assert.DoesNotThrow(() => counter.Close());
77+
}
78+
79+
private class TestDataHandlerCounter : DataHandlerCounter<String, DataHandlerCounterTest.SimpleData> {
80+
public TestDataHandlerCounter(DataHandlerCounterTest.TestDataHandler dataHandler)
81+
: base(dataHandler) {
82+
}
83+
}
84+
85+
private class SimpleData : EventData<String> {
86+
public SimpleData(String signature)
87+
: base(signature) {
88+
}
89+
}
90+
91+
private class SimpleDataFactory : IEventDataFactory<String, DataHandlerCounterTest.SimpleData> {
92+
public virtual DataHandlerCounterTest.SimpleData Create(IEvent @event, IMetaInfo metaInfo) {
93+
return new DataHandlerCounterTest.SimpleData(@event.GetEventType());
94+
}
95+
}
96+
97+
private class TestDataHandler : EventDataHandler<String, DataHandlerCounterTest.SimpleData> {
98+
private readonly AtomicLong processCount = new AtomicLong(0);
99+
100+
public TestDataHandler(long sleepTime)
101+
: base(new EventDataCacheComparatorBased<String, DataHandlerCounterTest.SimpleData>(new EventDataHandlerUtil.BiggerCountComparator
102+
<String, DataHandlerCounterTest.SimpleData>()), new DataHandlerCounterTest.SimpleDataFactory(), sleepTime
103+
, sleepTime) {
104+
}
105+
106+
public override void TryProcessNext() {
107+
processCount.IncrementAndGet();
108+
base.TryProcessNext();
109+
}
110+
111+
public override void TryProcessRest() {
112+
processCount.IncrementAndGet();
113+
base.TryProcessRest();
114+
}
115+
116+
protected internal override bool Process(DataHandlerCounterTest.SimpleData data) {
117+
return true;
118+
}
119+
120+
public virtual long GetProcessCount() {
121+
return processCount.Get();
122+
}
123+
}
124+
125+
private class TestDataHandlerWithException : DataHandlerCounterTest.TestDataHandler {
126+
public TestDataHandlerWithException(long sleepTime)
127+
: base(sleepTime) {
128+
}
129+
130+
public override void TryProcessNextAsync(bool? daemon) {
131+
throw new PdfException("Some exception message");
132+
}
133+
}
134+
135+
private class TestEvent : IEvent {
136+
private readonly String type;
137+
138+
public TestEvent(String type) {
139+
this.type = type;
140+
}
141+
142+
public virtual String GetEventType() {
143+
return type;
144+
}
145+
}
146+
}
147+
}

itext/itext.io/itext/io/LogMessageConstant.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,14 +319,18 @@ public const String IMAGE_HAS_INCORRECT_OR_UNSUPPORTED_BASE_COLOR_SPACE_IN_INDEX
319319

320320
public const String TYPOGRAPHY_NOT_FOUND = "Cannot find pdfCalligraph module, which was implicitly " + "required by one of the layout properties";
321321

322-
public const String UNABLE_TO_INVERT_GRADIENT_TRANSFORMATION = "Unable to invert gradient transformation, ignoring it";
323-
324322
public const String UNABLE_TO_APPLY_PAGE_DEPENDENT_PROP_UNKNOWN_PAGE_ON_WHICH_ELEMENT_IS_DRAWN = "Unable to apply page dependent property, because the page on which element is drawn is unknown. Usually this means that element was added to the Canvas instance that was created not with constructor taking PdfPage as argument. Not processed property: {0}";
325323

324+
public const String UNABLE_TO_INTERRUPT_THREAD = "Unable to interrupt a thread";
325+
326+
public const String UNABLE_TO_INVERT_GRADIENT_TRANSFORMATION = "Unable to invert gradient transformation, ignoring it";
327+
326328
public const String UNABLE_TO_REGISTER_EVENT_DATA_HANDLER_SHUTDOWN_HOOK = "Unable to register event data handler shutdown hook because of security reasons.";
327329

328330
public const String UNABLE_TO_SEARCH_FOR_EVENT_CONTEXT = "It is impossible to retrieve event context because of the security reasons. Event counting may behave in unexpected way";
329331

332+
public const String UNABLE_TO_UNREGISTER_EVENT_DATA_HANDLER_SHUTDOWN_HOOK = "Unable to unregister event data handler shutdown hook because of security permissions";
333+
330334
public const String UNEXPECTED_BEHAVIOUR_DURING_TABLE_ROW_COLLAPSING = "Unexpected behaviour during table row collapsing. Calculated rowspan was less then 1.";
331335

332336
public const String UNEXPECTED_EVENT_HANDLER_SERVICE_THREAD_EXCEPTION = "Unexpected exception encountered in service thread. Shutting it down.";

itext/itext.kernel/itext/kernel/PdfException.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,8 @@ public class PdfException : Exception {
206206

207207
public const String ContentStreamMustNotInvokeOperatorsThatSpecifyColorsOrOtherColorRelatedParameters = "Content stream must not invoke operators that specify colors or other color related parameters in the graphics state.";
208208

209+
public const String DataHandlerCounterHasBeenDisabled = "Data handler counter has been disabled";
210+
209211
public const String DecodeParameterType1IsNotSupported = "Decode parameter type {0} is not supported.";
210212

211213
public const String DefaultAppearanceNotFound = "DefaultAppearance is required but not found";

itext/itext.kernel/itext/kernel/counter/DataHandlerCounter.cs

Lines changed: 67 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ source product.
4141
For more information, please contact iText Software Corp. at this
4242
4343
*/
44+
using System;
45+
using iText.Kernel;
4446
using iText.Kernel.Counter.Context;
4547
using iText.Kernel.Counter.Data;
4648
using iText.Kernel.Counter.Event;
@@ -53,27 +55,87 @@ namespace iText.Kernel.Counter {
5355
/// <remarks>
5456
/// Counter based on
5557
/// <see cref="iText.Kernel.Counter.Data.EventDataHandler{T, V}"/>.
56-
/// Registers shutdown hook and thread for triggering event processing after wait time
58+
/// Registers shutdown hook and thread for triggering event processing after wait time.
5759
/// </remarks>
5860
/// <typeparam name="T">The data signature class</typeparam>
5961
/// <typeparam name="V">The event data class</typeparam>
60-
public class DataHandlerCounter<T, V> : EventCounter
62+
public class DataHandlerCounter<T, V> : EventCounter, IDisposable
6163
where V : EventData<T> {
64+
private volatile bool closed = false;
65+
6266
private readonly EventDataHandler<T, V> dataHandler;
6367

68+
/// <summary>
69+
/// Create an instance with provided data handler and
70+
/// <see cref="iText.Kernel.Counter.Context.UnknownContext.PERMISSIVE"/>
71+
/// fallback context.
72+
/// </summary>
73+
/// <param name="dataHandler">
74+
/// the
75+
/// <see cref="iText.Kernel.Counter.Data.EventDataHandler{T, V}"/>
76+
/// for events handling
77+
/// </param>
6478
public DataHandlerCounter(EventDataHandler<T, V> dataHandler)
6579
: this(dataHandler, UnknownContext.PERMISSIVE) {
6680
}
6781

82+
/// <summary>Create an instance with provided data handler and fallback context.</summary>
83+
/// <param name="dataHandler">
84+
/// the
85+
/// <see cref="iText.Kernel.Counter.Data.EventDataHandler{T, V}"/>
86+
/// for events handling
87+
/// </param>
88+
/// <param name="fallback">
89+
/// the fallback
90+
/// <see cref="iText.Kernel.Counter.Context.IContext">context</see>
91+
/// </param>
6892
public DataHandlerCounter(EventDataHandler<T, V> dataHandler, IContext fallback)
6993
: base(fallback) {
7094
this.dataHandler = dataHandler;
71-
EventDataHandlerUtil.RegisterProcessAllShutdownHook<T, V>(dataHandler);
72-
EventDataHandlerUtil.RegisterTimedProcessing<T, V>(dataHandler);
95+
EventDataHandlerUtil.RegisterProcessAllShutdownHook<T, V>(this.dataHandler);
96+
EventDataHandlerUtil.RegisterTimedProcessing<T, V>(this.dataHandler);
7397
}
7498

99+
/// <summary>Process the event.</summary>
100+
/// <param name="event">
101+
///
102+
/// <see cref="iText.Kernel.Counter.Event.IEvent"/>
103+
/// to count
104+
/// </param>
105+
/// <param name="metaInfo">
106+
/// the
107+
/// <see cref="iText.Kernel.Counter.Event.IMetaInfo"/>
108+
/// that can hold information about event origin
109+
/// </param>
75110
protected internal override void OnEvent(IEvent @event, IMetaInfo metaInfo) {
76-
dataHandler.Register(@event, metaInfo);
111+
if (this.closed) {
112+
throw new InvalidOperationException(PdfException.DataHandlerCounterHasBeenDisabled);
113+
}
114+
this.dataHandler.Register(@event, metaInfo);
115+
}
116+
117+
/// <summary>Disable all registered hooks and process the left data.</summary>
118+
/// <remarks>
119+
/// Disable all registered hooks and process the left data. Note that after this method
120+
/// invocation the
121+
/// <see cref="DataHandlerCounter{T, V}.OnEvent(iText.Kernel.Counter.Event.IEvent, iText.Kernel.Counter.Event.IMetaInfo)
122+
/// "/>
123+
/// method would throw
124+
/// an exception.
125+
/// </remarks>
126+
public virtual void Close() {
127+
this.closed = true;
128+
try {
129+
EventDataHandlerUtil.DisableShutdownHooks<T, V>(this.dataHandler);
130+
EventDataHandlerUtil.DisableTimedProcessing<T, V>(this.dataHandler);
131+
}
132+
finally {
133+
this.dataHandler.TryProcessRest();
134+
}
135+
}
136+
137+
void System.IDisposable.Dispose() {
138+
Close();
77139
}
78140
}
79141
}

0 commit comments

Comments
 (0)