From a32acfe8f84c70d4b131ea3a210677b3b5ec14df Mon Sep 17 00:00:00 2001 From: Christian Wulf <chw@informatik.uni-kiel.de> Date: Tue, 3 Jun 2014 18:24:08 +0200 Subject: [PATCH] init --- .classpath | 26 ++ .gitignore | 2 + .project | 23 ++ .settings/org.eclipse.core.resources.prefs | 4 + .settings/org.eclipse.jdt.core.prefs | 8 + .settings/org.eclipse.m2e.core.prefs | 4 + teetime/pom.xml => pom.xml | 22 +- results/chw-work/Kieker.csv | 20 + results/chw-work/TeeTime.csv | 20 + results/evaluation.xlsx | Bin 0 -> 10415 bytes src/main/java/experiment/Experiment1.java | 232 ++++++++++++ src/main/java/experiment/Experiment2.java | 263 ++++++++++++++ .../analysis/examples/ThroughputAnalysis.java | 66 ++++ .../examples/ThroughputTimestampAnalysis.java | 83 +++++ .../kieker/analysis/stage/CacheFilter.java | 64 ++++ .../kieker/analysis/stage/CollectorSink.java | 59 +++ .../analysis/stage/EmptyPassOnFilter.java | 51 +++ .../analysis/stage/MappingFileParser.java | 129 +++++++ .../kieker/analysis/stage/ObjectProducer.java | 88 +++++ .../stage/RecordFromBinaryFileCreator.java | 106 ++++++ .../stage/RecordFromTextLineCreator.java | 113 ++++++ .../analysis/stage/StartTimestampFilter.java | 47 +++ .../analysis/stage/StopTimestampFilter.java | 47 +++ .../ConcurrentCountWordsAnalysis.java | 341 ++++++++++++++++++ .../countWords/CountWordsAnalysis.java | 150 ++++++++ .../examples/countWords/CountWordsStage.java | 80 ++++ .../countWords/DirectoryName2Files.java | 66 ++++ .../countWords/OutputWordsCountSink.java | 69 ++++ .../countWords/QueuedCountWordsAnalysis.java | 158 ++++++++ .../CountingObjectsAnalysis.java | 138 +++++++ .../recordReader/RecordReaderAnalysis.java | 168 +++++++++ .../throughput/ThroughputAnalysis.java | 122 +++++++ .../ThroughputTimestampAnalysis.java | 173 +++++++++ .../examples/throughput/TimestampObject.java | 52 +++ .../traceReconstruction/TraceAnalysis.java | 66 ++++ .../TraceReconstructionAnalysis.java | 108 ++++++ .../TraceReconstructionAnalysis2.java | 143 ++++++++ .../concurrent/ConcurrentPipeline.java.todo | 157 ++++++++ .../ConcurrentWorkStealingPipe.java | 145 ++++++++ .../ConcurrentWorkStealingPipeFactory.java | 46 +++ .../framework/concurrent/IStageScheduler.java | 35 ++ .../framework/concurrent/IStageWorkList.java | 40 ++ .../concurrent/NextStageScheduler.java | 136 +++++++ .../SingleProducerSingleConsumerPipe.java | 84 +++++ .../concurrent/StageTerminationPolicy.java | 30 ++ .../concurrent/StageWorkArrayList.java | 179 +++++++++ .../framework/concurrent/StageWorkList.java | 81 +++++ .../framework/concurrent/TokenBundle.java | 16 + .../framework/concurrent/WorkerThread.java | 207 +++++++++++ .../concurrent/steal/IStealStrategy.java | 33 ++ .../steal/StealIfEmptyStrategy.java | 42 +++ .../steal/StealIfMayBeDisabledStrategy.java | 44 +++ .../framework/core/AbstractFilter.java | 334 +++++++++++++++++ .../framework/core/AbstractMultiPort.java | 49 +++ .../teetime/framework/core/AbstractPipe.java | 115 ++++++ .../teetime/framework/core/AbstractPort.java | 37 ++ .../teetime/framework/core/AbstractStage.java | 71 ++++ .../java/teetime/framework/core/Analysis.java | 37 ++ .../framework/core/CompositeFilter.java | 33 ++ .../java/teetime/framework/core/Context.java | 169 +++++++++ .../teetime/framework/core/Description.java | 34 ++ .../teetime/framework/core/IBaseStage.java | 25 ++ .../teetime/framework/core/IInputPort.java | 40 ++ .../teetime/framework/core/IOutputPort.java | 14 + .../java/teetime/framework/core/IPipe.java | 123 +++++++ .../teetime/framework/core/IPipeCommand.java | 26 ++ .../teetime/framework/core/IPipeline.java | 34 ++ .../java/teetime/framework/core/IPort.java | 22 ++ .../teetime/framework/core/IPortListener.java | 26 ++ .../java/teetime/framework/core/ISink.java | 30 ++ .../java/teetime/framework/core/ISource.java | 27 ++ .../java/teetime/framework/core/IStage.java | 178 +++++++++ .../teetime/framework/core/InputPortImpl.java | 45 +++ .../framework/core/OutputPortImpl.java | 10 + .../java/teetime/framework/core/Pipeline.java | 58 +++ .../framework/sequential/MethodCallPipe.java | 87 +++++ .../framework/sequential/Pipeline.java.todo | 190 ++++++++++ .../framework/sequential/QueuePipe.java | 78 ++++ .../util/BaseStage2StageExtractor.java | 49 +++ .../java/teetime/framework/util/Cloner.java | 128 +++++++ src/main/java/teetime/stage/Cache.java | 90 +++++ src/main/java/teetime/stage/Clock.java | 45 +++ .../java/teetime/stage/CollectorSink.java | 67 ++++ .../java/teetime/stage/CountingFilter.java | 58 +++ .../teetime/stage/FileExtensionFilter.java | 78 ++++ .../java/teetime/stage/InstanceOfFilter.java | 53 +++ .../java/teetime/stage/MappingException.java | 32 ++ src/main/java/teetime/stage/NoopFilter.java | 45 +++ .../teetime/stage/StartTimestampFilter.java | 48 +++ .../teetime/stage/StopTimestampFilter.java | 48 +++ .../java/teetime/stage/SuperTypeFilter.java | 23 ++ .../java/teetime/stage/TypeLoggerFilter.java | 71 ++++ src/main/java/teetime/stage/basic/Delay.java | 73 ++++ .../teetime/stage/basic/ObjectProducer.java | 85 +++++ .../teetime/stage/basic/RepeaterSource.java | 75 ++++ .../basic/distributor/CloneStrategy.java | 34 ++ .../distributor/CopyByReferenceStrategy.java | 39 ++ .../stage/basic/distributor/Distributor.java | 60 +++ .../distributor/IDistributorStrategy.java | 32 ++ .../basic/distributor/RoundRobinStrategy.java | 48 +++ .../stage/basic/merger/IMergerStrategy.java | 32 ++ .../teetime/stage/basic/merger/Merger.java | 65 ++++ .../basic/merger/RoundRobinStrategy.java | 54 +++ .../stage/composite/CycledCountingFilter.java | 57 +++ .../ReadRecordFromCsvFileFilter.java | 65 ++++ .../teetime/stage/composite/TeeFilter.java | 56 +++ src/main/java/teetime/stage/io/DbReader.java | 186 ++++++++++ .../stage/io/Directory2FilesFilter.java | 116 ++++++ .../stage/io/File2TextLinesFilter.java | 89 +++++ src/main/java/teetime/stage/io/Printer.java | 144 ++++++++ src/main/java/teetime/stage/io/TCPReader.java | 206 +++++++++++ .../stage/kieker/File2RecordFilter.java | 144 ++++++++ .../kieker/MonitoringLogDirectory2Files.java | 85 +++++ .../kieker/className/ClassNameRegistry.java | 29 ++ .../ClassNameRegistryCreationFilter.java | 94 +++++ .../ClassNameRegistryRepository.java | 61 ++++ .../fileToRecord/BinaryFile2RecordFilter.java | 113 ++++++ .../fileToRecord/DatFile2RecordFilter.java | 57 +++ .../fileToRecord/ZipFile2RecordFilter.java | 134 +++++++ .../TextLine2MappingRegistryFilter.java | 76 ++++ .../textLine/TextLine2RecordFilter.java | 117 ++++++ .../TraceReconstructionFilter.java | 191 ++++++++++ .../predicate/FileExtensionPredicate.java | 39 ++ .../stage/predicate/IsDirectoryPredicate.java | 33 ++ .../predicate/IsIMonitoringRecordInRange.java | 39 ++ .../predicate/IsInstanceOfPredicate.java | 47 +++ .../IsOperationExecutionRecordInRange.java | 42 +++ ...rationExecutionRecordTraceIdPredicate.java | 41 +++ .../stage/predicate/IsSuperTypePredicate.java | 47 +++ .../stage/predicate/IsTimestampInRange.java | 42 +++ .../stage/predicate/IsTraceIdPredicate.java | 44 +++ .../stage/predicate/PredicateFilter.java | 72 ++++ .../stringBuffer/StringBufferFilter.java | 93 +++++ .../handler/AbstractDataTypeHandler.java | 55 +++ .../handler/IMonitoringRecordHandler.java | 59 +++ .../stringBuffer/handler/StringHandler.java | 35 ++ .../throughput/AnalysisThroughputFilter.java | 60 +++ .../throughput/ThroughputAnalysisResult.java | 44 +++ .../java/teetime/stage/util/TextLine.java | 45 +++ .../visualization/IWebVisualizationSink.java | 35 ++ .../PlainTextWebVisualizationSink.java | 73 ++++ .../java/teetime/util/CyclicListIterator.java | 54 +++ src/main/java/teetime/util/MathUtil.java | 49 +++ src/main/java/teetime/util/Pair.java | 25 ++ .../java/teetime/util/StacklessException.java | 12 + .../java/teetime/util/StatisticsUtil.java | 112 ++++++ src/main/java/teetime/util/StopWatch.java | 19 + .../util/concurrent/P1C1QueueOriginal3.java | 198 ++++++++++ .../util/concurrent/PaddedAtomicLong.java | 28 ++ .../hashmap/ConcurrentHashMapWithDefault.java | 57 +++ .../util/concurrent/hashmap/TraceBuffer.java | 152 ++++++++ .../util/concurrent/hashmap/ValueFactory.java | 31 ++ .../workstealing/CircularArray.java | 79 ++++ .../CircularWorkStealingDeque.java | 311 ++++++++++++++++ .../workstealing/DequePopException.java | 8 + ...CircularWorkStealingDequeWithSentinel.java | 202 +++++++++++ ...kStealingDequeWithThreadLocalSentinel.java | 226 ++++++++++++ .../ExceptionalCircularWorkStealingDeque.java | 196 ++++++++++ .../UntypedCircularWorkStealingDeque.java | 182 ++++++++++ ...dExceptionalCircularWorkStealingDeque.java | 195 ++++++++++ .../analysis/stage/EmptyPassOnFilterTest.java | 60 +++ .../RecordReaderAnalysisTest.java | 127 +++++++ ...hodCallThoughputTimestampAnalysisTest.java | 73 ++++ .../throughput/ThroughputAnalysisTest.java | 82 +++++ .../ThroughputTimestampAnalysisTest.java | 87 +++++ .../CircularWorkStealingDequeTest.java | 32 ++ teetime/src/main/java/teetime/App.java | 13 - teetime/src/test/java/teetime/AppTest.java | 38 -- 168 files changed, 13471 insertions(+), 52 deletions(-) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 .settings/org.eclipse.core.resources.prefs create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.m2e.core.prefs rename teetime/pom.xml => pom.xml (56%) create mode 100644 results/chw-work/Kieker.csv create mode 100644 results/chw-work/TeeTime.csv create mode 100644 results/evaluation.xlsx create mode 100644 src/main/java/experiment/Experiment1.java create mode 100644 src/main/java/experiment/Experiment2.java create mode 100644 src/main/java/kieker/analysis/examples/ThroughputAnalysis.java create mode 100644 src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java create mode 100644 src/main/java/kieker/analysis/stage/CacheFilter.java create mode 100644 src/main/java/kieker/analysis/stage/CollectorSink.java create mode 100644 src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java create mode 100644 src/main/java/kieker/analysis/stage/MappingFileParser.java create mode 100644 src/main/java/kieker/analysis/stage/ObjectProducer.java create mode 100644 src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java create mode 100644 src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java create mode 100644 src/main/java/kieker/analysis/stage/StartTimestampFilter.java create mode 100644 src/main/java/kieker/analysis/stage/StopTimestampFilter.java create mode 100644 src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java create mode 100644 src/main/java/teetime/examples/countWords/CountWordsAnalysis.java create mode 100644 src/main/java/teetime/examples/countWords/CountWordsStage.java create mode 100644 src/main/java/teetime/examples/countWords/DirectoryName2Files.java create mode 100644 src/main/java/teetime/examples/countWords/OutputWordsCountSink.java create mode 100644 src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java create mode 100644 src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java create mode 100644 src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java create mode 100644 src/main/java/teetime/examples/throughput/ThroughputAnalysis.java create mode 100644 src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java create mode 100644 src/main/java/teetime/examples/throughput/TimestampObject.java create mode 100644 src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java create mode 100644 src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java create mode 100644 src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java create mode 100644 src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo create mode 100644 src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java create mode 100644 src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java create mode 100644 src/main/java/teetime/framework/concurrent/IStageScheduler.java create mode 100644 src/main/java/teetime/framework/concurrent/IStageWorkList.java create mode 100644 src/main/java/teetime/framework/concurrent/NextStageScheduler.java create mode 100644 src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java create mode 100644 src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java create mode 100644 src/main/java/teetime/framework/concurrent/StageWorkArrayList.java create mode 100644 src/main/java/teetime/framework/concurrent/StageWorkList.java create mode 100644 src/main/java/teetime/framework/concurrent/TokenBundle.java create mode 100644 src/main/java/teetime/framework/concurrent/WorkerThread.java create mode 100644 src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java create mode 100644 src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java create mode 100644 src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java create mode 100644 src/main/java/teetime/framework/core/AbstractFilter.java create mode 100644 src/main/java/teetime/framework/core/AbstractMultiPort.java create mode 100644 src/main/java/teetime/framework/core/AbstractPipe.java create mode 100644 src/main/java/teetime/framework/core/AbstractPort.java create mode 100644 src/main/java/teetime/framework/core/AbstractStage.java create mode 100644 src/main/java/teetime/framework/core/Analysis.java create mode 100644 src/main/java/teetime/framework/core/CompositeFilter.java create mode 100644 src/main/java/teetime/framework/core/Context.java create mode 100644 src/main/java/teetime/framework/core/Description.java create mode 100644 src/main/java/teetime/framework/core/IBaseStage.java create mode 100644 src/main/java/teetime/framework/core/IInputPort.java create mode 100644 src/main/java/teetime/framework/core/IOutputPort.java create mode 100644 src/main/java/teetime/framework/core/IPipe.java create mode 100644 src/main/java/teetime/framework/core/IPipeCommand.java create mode 100644 src/main/java/teetime/framework/core/IPipeline.java create mode 100644 src/main/java/teetime/framework/core/IPort.java create mode 100644 src/main/java/teetime/framework/core/IPortListener.java create mode 100644 src/main/java/teetime/framework/core/ISink.java create mode 100644 src/main/java/teetime/framework/core/ISource.java create mode 100644 src/main/java/teetime/framework/core/IStage.java create mode 100644 src/main/java/teetime/framework/core/InputPortImpl.java create mode 100644 src/main/java/teetime/framework/core/OutputPortImpl.java create mode 100644 src/main/java/teetime/framework/core/Pipeline.java create mode 100644 src/main/java/teetime/framework/sequential/MethodCallPipe.java create mode 100644 src/main/java/teetime/framework/sequential/Pipeline.java.todo create mode 100644 src/main/java/teetime/framework/sequential/QueuePipe.java create mode 100644 src/main/java/teetime/framework/util/BaseStage2StageExtractor.java create mode 100644 src/main/java/teetime/framework/util/Cloner.java create mode 100644 src/main/java/teetime/stage/Cache.java create mode 100644 src/main/java/teetime/stage/Clock.java create mode 100644 src/main/java/teetime/stage/CollectorSink.java create mode 100644 src/main/java/teetime/stage/CountingFilter.java create mode 100644 src/main/java/teetime/stage/FileExtensionFilter.java create mode 100644 src/main/java/teetime/stage/InstanceOfFilter.java create mode 100644 src/main/java/teetime/stage/MappingException.java create mode 100644 src/main/java/teetime/stage/NoopFilter.java create mode 100644 src/main/java/teetime/stage/StartTimestampFilter.java create mode 100644 src/main/java/teetime/stage/StopTimestampFilter.java create mode 100644 src/main/java/teetime/stage/SuperTypeFilter.java create mode 100644 src/main/java/teetime/stage/TypeLoggerFilter.java create mode 100644 src/main/java/teetime/stage/basic/Delay.java create mode 100644 src/main/java/teetime/stage/basic/ObjectProducer.java create mode 100644 src/main/java/teetime/stage/basic/RepeaterSource.java create mode 100644 src/main/java/teetime/stage/basic/distributor/CloneStrategy.java create mode 100644 src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java create mode 100644 src/main/java/teetime/stage/basic/distributor/Distributor.java create mode 100644 src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java create mode 100644 src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java create mode 100644 src/main/java/teetime/stage/basic/merger/IMergerStrategy.java create mode 100644 src/main/java/teetime/stage/basic/merger/Merger.java create mode 100644 src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java create mode 100644 src/main/java/teetime/stage/composite/CycledCountingFilter.java create mode 100644 src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java create mode 100644 src/main/java/teetime/stage/composite/TeeFilter.java create mode 100644 src/main/java/teetime/stage/io/DbReader.java create mode 100644 src/main/java/teetime/stage/io/Directory2FilesFilter.java create mode 100644 src/main/java/teetime/stage/io/File2TextLinesFilter.java create mode 100644 src/main/java/teetime/stage/io/Printer.java create mode 100644 src/main/java/teetime/stage/io/TCPReader.java create mode 100644 src/main/java/teetime/stage/kieker/File2RecordFilter.java create mode 100644 src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java create mode 100644 src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java create mode 100644 src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java create mode 100644 src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java create mode 100644 src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java create mode 100644 src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java create mode 100644 src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java create mode 100644 src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java create mode 100644 src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java create mode 100644 src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java create mode 100644 src/main/java/teetime/stage/predicate/FileExtensionPredicate.java create mode 100644 src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java create mode 100644 src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java create mode 100644 src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java create mode 100644 src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java create mode 100644 src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java create mode 100644 src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java create mode 100644 src/main/java/teetime/stage/predicate/IsTimestampInRange.java create mode 100644 src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java create mode 100644 src/main/java/teetime/stage/predicate/PredicateFilter.java create mode 100644 src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java create mode 100644 src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java create mode 100644 src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java create mode 100644 src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java create mode 100644 src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java create mode 100644 src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java create mode 100644 src/main/java/teetime/stage/util/TextLine.java create mode 100644 src/main/java/teetime/stage/visualization/IWebVisualizationSink.java create mode 100644 src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java create mode 100644 src/main/java/teetime/util/CyclicListIterator.java create mode 100644 src/main/java/teetime/util/MathUtil.java create mode 100644 src/main/java/teetime/util/Pair.java create mode 100644 src/main/java/teetime/util/StacklessException.java create mode 100644 src/main/java/teetime/util/StatisticsUtil.java create mode 100644 src/main/java/teetime/util/StopWatch.java create mode 100644 src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java create mode 100644 src/main/java/teetime/util/concurrent/PaddedAtomicLong.java create mode 100644 src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java create mode 100644 src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java create mode 100644 src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/CircularArray.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/DequePopException.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java create mode 100644 src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java create mode 100644 src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java create mode 100644 src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java create mode 100644 src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java create mode 100644 src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java create mode 100644 src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java create mode 100644 src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java delete mode 100644 teetime/src/main/java/teetime/App.java delete mode 100644 teetime/src/test/java/teetime/AppTest.java diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..fd7ad7fb --- /dev/null +++ b/.classpath @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" output="target/classes" path="src/main/java"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="src" output="target/test-classes" path="src/test/java"> + <attributes> + <attribute name="optional" value="true"/> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER"> + <attributes> + <attribute name="maven.pomderived" value="true"/> + </attributes> + </classpathentry> + <classpathentry kind="output" path="target/classes"/> +</classpath> diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..934e0e06 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/bin +/target diff --git a/.project b/.project new file mode 100644 index 00000000..bdb6e353 --- /dev/null +++ b/.project @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>teetime</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.m2e.core.maven2Builder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.m2e.core.maven2Nature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..f9fe3459 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +encoding//src/main/java=UTF-8 +encoding//src/test/java=UTF-8 +encoding/<project>=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..69c31cd4 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,8 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.compliance=1.6 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.source=1.6 diff --git a/.settings/org.eclipse.m2e.core.prefs b/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 00000000..f897a7f1 --- /dev/null +++ b/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/teetime/pom.xml b/pom.xml similarity index 56% rename from teetime/pom.xml rename to pom.xml index e50c55b3..1edcb345 100644 --- a/teetime/pom.xml +++ b/pom.xml @@ -18,8 +18,28 @@ <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> - <version>3.8.1</version> + <version>4.11</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + <version>1.3</version> + </dependency> + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-library</artifactId> + <version>1.3</version> + </dependency> + <dependency> + <groupId>net.kieker-monitoring</groupId> + <artifactId>kieker</artifactId> + <version>1.9</version> + </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>17.0</version> + </dependency> </dependencies> </project> diff --git a/results/chw-work/Kieker.csv b/results/chw-work/Kieker.csv new file mode 100644 index 00000000..0b207487 --- /dev/null +++ b/results/chw-work/Kieker.csv @@ -0,0 +1,20 @@ +50;222974790;249344303;255853039;259406468;302300903;256949018;3867545 +100;479268312;501520758;514242657;520879474;547129527;512607537;3861798 +150;769605950;814607155;827869293;840828881;997575901;832140772;10406475 +200;1053294865;1114439212;1133247837;1174248560;1374436002;1150302103;17160918 +250;1347713034;1403914765;1423028403;1445675354;1514311623;1429062984;10029167 +300;1634709890;1692022888;1716632518;1733769931;1845717310;1714584838;10851185 +350;1898198121;1979121553;2005629409;2021975347;2104626641;2002653027;12416337 +400;2184222053;2264418050;2297249500;2347125585;2571002690;2312469267;20003529 +450;2114346436;2474942703;2513493875;2554615702;2821436913;2484613004;39023338 +500;2714958023;2807430509;2834917858;2855149332;2908789030;2833729060;9892356 +550;2953534484;3129759679;3152678393;3207891190;3355362594;3171670501;21159351 +600;3375834220;3456305673;3507196145;3564639277;4045959350;3525562922;30257420 +650;3733499812;3793166710;3842865043;3931548874;4501397192;3901445690;47083708 +700;3902291379;4145148703;4226156701;4324776258;5536500029;4373255110;108796825 +750;4488138338;4536918344;4577786471;4664000637;4766312788;4598726882;21193905 +800;4787523343;4899780250;4954223740;5032639329;5227891132;4974727780;27106090 +850;5330114612;5428669718;5497654926;5559696251;5744444585;5503212946;26634323 +900;4915166811;5759447724;5940546980;6032643433;7847659872;6004221609;120237698 +950;6278789404;6558588268;6770077633;7249897679;10036245303;7069647198;217586495 +1000;6998942511;7251777843;7319524934;7434979353;8077187734;7366103358;56194326 diff --git a/results/chw-work/TeeTime.csv b/results/chw-work/TeeTime.csv new file mode 100644 index 00000000..f2ab7802 --- /dev/null +++ b/results/chw-work/TeeTime.csv @@ -0,0 +1,20 @@ +50;81005535;92166652;93302961;95672050;103493206;93511502;1038983 +100;174480091;183187963;185029128;188204061;194225513;185294239;1154436 +150;239712770;274010174;277981097;280009048;295593477;276621639;2183336 +200;337821234;367758134;371051706;377328500;478632833;374718182;5375113 +250;385948931;442055422;457899298;465820620;524613120;452422321;7162054 +300;514072703;542246436;547270333;553017151;564887640;546601274;2986762 +350;615707092;645556962;650576755;657115458;671433855;651241364;2766588 +400;700708593;734629552;740820958;745605936;754731302;739358059;3082558 +450;803342998;831226084;835117780;840909342;850780207;835062277;2948398 +500;876183364;920951807;930805839;937619999;963086376;929382628;4780910 +550;1003258678;1028639258;1035441512;1042563969;1072049712;1035805640;3409157 +600;1093110018;1131170625;1137555794;1147802691;1268041677;1142788571;7089446 +650;1181869795;1215763661;1224030638;1233324726;1278020097;1223997024;4308705 +700;1277366144;1316095891;1326646981;1336546582;1378479587;1327486503;4854941 +750;1372059934;1404826111;1418028315;1430832318;1457884930;1416883852;5369178 +800;1474965281;1493768979;1500705882;1512160518;1543885627;1503343105;3938081 +850;1546340924;1592571626;1605533267;1613038736;1629217593;1601614338;4648053 +900;1604224130;1687782667;1702500907;1711553611;1828215358;1701607220;9538072 +950;1737332802;1772582603;1787626793;1810397720;1878440793;1792495376;8716251 +1000;1835066054;1875513813;1889568248;1905512700;1982777611;1896242150;8591685 diff --git a/results/evaluation.xlsx b/results/evaluation.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2d32e884ec9f8629de80baca0fbb8a15e4f439db GIT binary patch literal 10415 zcmeHt1y@|j)^_7AL4&)60F6a(3+}<427-HI0fIwt57Gn(65Ktw1&847?!h(ic1CVy znEAdxaBr<X=d81;_j9UtZL7UYQ3ehk4}b_j1^@t5famOu(cUlsfIk8NfD1r|)fTn2 z0h`!>^;O*LOdNGtU9GJsGvHwv(*dy1|NpQ37jvLcMaH%bh~A_y<CW|1ai#@xluPNw zp8Z^ufgmMUpm?Pe9tq<8I~!LsQK5jPkzA=;e$v^-*8JiaI(V+ZjSPsB7;LrO%#<c; zQy0Z{g=AFRc)kOHLm48T-`x*g+lF&*6bbGAS5j;GSsfYvT80~Z8UjgQ6q#2Rfw6k{ zj@)opQ`>w!ZJMw$Ox%w%7Tx{wv{d36N?hGDjX{Nyde1M2^K}>xnO`%4J~^15!AR|V ze*N0pxijO<7P==Oy9_@4ORop8|FO7G$5+Mf%oLa>4PoHjC#&6z=rXHEK1FE;X7cj& zGhaQA9N{ymi1LNf^4}afsP<40Zs2ZiOuI$NyvBVUH{Lt-@qqL8h@-a_Z2L6ph|otH zj6M0d`?66_#NV7sOiSJSxpO^?wGpc-fS<Z}VR``Jw$o-<*rJUn%Dzru^In_NH+6OM z13BVmbdI=ppT6N_h8%c@2pN`W>iKpVXp!@{yDsIs0k$ybDAyLv9LM0dJ2t4y-QU9j z6#u5URjNRmGiZ-2R4>t>imUHnV&%xj`t$xjh5awq(!WeSEM`c)9f<z*P_jXE?PkYD z68Tehy{u$Cl?r(a?EytqcqXmT#koJ6ue8zZ@cHJDp!UMShPes_DTqzJ{R^XT87Ihn z(!#YUO${p@L~Ux0*F_vbYN$THV5RSx6$b%L-sF~th7x~?yQKkfktyJz9ql<}M+235 z4b6sQiE>Tm;@0^Co5>Yf_{Li;kdfSx@&&H>uF7k;<ZNBpHatNI@?k=$xV(MN>!NSa z@`h$}lSTTZ{P0gwMj!RuP#eLe($DB9oZeTzu&0&@{wBF}!nYXWD73rl!<uH6ciA|| zx02((EN6Ua*xHGuqW`a2;v*23a0>1D0@V^?05XiL72EHM;$rJy39_}d{ORESR2mql zmxH>(|L#^8GbG&s#Qu8ddx*VZGg?ed4LNw_-)Q0K9VH7Xx5nXEO>1iWlrbA%(s+t< z`KJ5g2S?KWfhB1N68(;!x*|Ru@+tk)f(z<G@+JfcyJJS1A3hS5o_1<{Qhk<?lxtIF z?OKw$-c@-iaWyl}m^30e1bcW>o@6BVW#3p&?trih<8u=?w$Tx|9bAR78q|@61z)qz ziLv(=nAWo-9;h3hVne3Xv`1WPNbkjSFg05vSvhFBPmUnmt1lxFBDX8zPBe8vxf8~h z=-Wb=(UGKHbTN*b)cy0xz%{VQO)pc)?6LNT^~Ij<*zkg8=xYDdvKlE(AL&7RxS%qF z1r-LU%=}ea!lHE*+JJb0hjO=A19b`E?20I4!@1EUqOfgFx<eYGldRT;OC=XpXj|Fp z{1p3+gE!941XYvChTTjsf|e-}1L<p&lgNUG&SvM~fE51GBHDhWgIk8F^9%jNyXX27 z2ShMon~H&o3Yp!TbZTu_R^fp>0Z#GAabI-TEQ3U@aoAOOq*q*VR7HrkP+nh91hSyU zuZ9j_;U|9+CAD~D=Bb}QBl?Uv?yFqM``|Bk*!cUNBQ2HRmsRt<W7-pyXP0cL5H>K+ zPK3>O1R_=*iA}4njj(4Rf@N|ds0CwRx6l%-q_-!->u)IWOT`+GG+7T!Gg{rUeU_Ux zF9@r|Kq|lX2#)2-4o@zwQ00gtjamM&rR6SB#S9nJ^Ma`6`xaKCW87yj1Lwy%)m@JL zsd0j=_n_yhc(nnf&i;KBI$J5HRTA#(HW0N{X-^tQ4LjGJLwy0hn}1yjHRdc?DQHg{ zbSdzDUkXPvLkAP%*I);88&k)h+8d|b2eoq84KznsALz2E$LLv}u+imFT3|_5A}iY$ z7IuWotyTq3#=Uzi*pjfEPZ&p!6p=bT+#m5g*&{E+<9J3IgPjahMEb?rmng-r;G}`; z=W!awilx$Uk=qg4JW}!s)Rxh%EK`Z7VvdCH&j@wcdA|kvnNECBL;q313TJ6Ch|2sz zLKPts*no-`D<oA+W+2Pf7z%f_?D`fESi==uC`&CH<<4LbA^}6MREmVv;c-=|#m6T< zy5|(nqPE?4DgYtJc*#9E({CK`u7|!)KxX9Ts(Mq7n$NOJViglKmtA|HjNZ;FRcy$D zc8_r@N5Ckl0Beqx6l$%%wF*id-Qb~g^D5Yt!+nmfQB^U5sW>(h-}Zu*Kd#qhFz#jk zdfMut_Jl<})&hY9i7kuUe)^hS-1ss=`9K;c@+}5^GnBFa)B@q=OM_`>PcM}HG{3X{ zrxrMxnV5hb|1hGzy|Dksgce4({`5COhmyCVYrInRa3W^gtjOl-k13Y2ZAgZBl&r9C z+<l!tjncBFJe@s0iEW(sWU08yqGm4(ns{dLUUY{hl6(Pk$OX$lEZRK<H#P_tW$()@ zWFKuH*GckoVy6mfXpj`QAP<9K214g&Ssh-{CukOOeOnxXE~Guy$r}sti|AWwvK5E? zah}aEjxW}~T-iDk2R`0n3WyBQC@HiH=}LNn-6y%zI^+0i@Kcp4!JJd%xgftoaG{Ex zR28|Fnm8f9OLWco<12}gB7tw7aR0`cY_Nn!6e0kSP4aWg^qYMEo0(Xfu>E%bjU9WM z3brJ#@S16k$i&@kjToGXC7kl7%1F$VGp1N}>>J1r<@xI?l8G43FsdYIX~l$fqH67h zgtFCElm+C{kweBxS|CaDXY$T+G0$LO&Zwq8UJcybFEk`IzFQUU`1a^g%yI4hc*z8x z7$_~$oV2o$xBV_214}AkmHiDLy|t};mW$Euyi4(_9DdAuN)3uWxw&(Z?3jUg{Oxw& z2X!|QP$I<cks(3}wFVKI2RfLhx?HQEs6;IBD|v`Lyzxb8x-!U?Idj%Xh)+!!>rxh- zOH~<$Tw#}(db7L_o~NyFA10DrkUR#dR}@_a0oS_Z&AWx(i8K6$Bvz0`&rI8wrmO7T z5roz4{ADIzrNPPt+sfkowNni5JdWbYT_^XmlvEglMI_atr>~O3@5CEpho5@y86`Q_ zOcZkKc=+6DNo}a*jG9*~Yd%$iD~}Avd6!weFs0}89ntt`0H<kq`oMXTtN}*z_ASa{ z^Msadtt%WVQFsnrrL$Rcn(`18?w~XCG6`TYH~TvRY6cY++W@G|sETWf+9NGgfRZc5 z@Rd>421=3vg-C5^W^}rSV+#bH@XcNnn~Z2OQA|^Mu!Z{}!{ZS{;luHhLp^@45a{J_ z{C&EookD?}pU3UeFMIvzT&KOF&gC&g;r?=m=JaN?L|@>pftW4yQorf`ax+b!08s>$ zh4$@HXRLl2zuVC|L$l{iEo>9Z_grL(k@u2d2x+D$xsT0PVDXwUEo(LCF=(f-h3Z=| z<^1bUa1H!jg{YHM#YYFIx#X%Xkcyj#LO;cRyrLx!<}lJFfz879SJ^N&=6VQdSf=)h z13LX7wW48>`K~VZd$rZiWzucl2yag{k>E)5!vz`zz~SHqoRX&DD1VygmnN$}c&%^O z!+<)Und@hsEfok}gC7A6vxc{@ojm#2Iv!ztewcx2s;YGuMMH>!;-eYxU9^pqbD@JX zAoEF#C}~+n>qRMQfj?<~fPTkW7OVo2zx@~qZewPA<{cc&H9^WdG||?4@QnaOf>oK3 zrzoQ?=p`Gq%Vj0W3S(XyrWq+g*<lZpKchI3j%w>g9fazd?ACiV`=CiCc=ezHt(A=U z1vLw-*igS%0KP5GLC2xNAbn!u6FBbqys<~&_xV<ws7|vEuG-bJ3(u@Tsy!9%`5J8P zKhR`OF;hE{;L9^o3T>HBXHoZmY%-sg>onbdX<+PB%ytq5L99pLO7!s_qL^5vZ-nhh zje5vT`F!<!Jcc$99lIt`M(Z!}vA3nQ6#*8)EopZ~rMgVnfx>m*q=1WJEX@<exQrto z_NF$b`m5+k$(~z9o&t3>ShDc^Y6`PtdwH)VI+k4-VM7DLt~Clj*k`q`C5;DmCwt?> zGFDeN9&Yye3D2BEd{ywkFsW>60is_W_opi1Z`eNZlgr;qG~0cUI5{SqYXo}dKn-?D ztT9yNv11ZihvktwU!^n)<M4*xP8od5>EbL@TbdX#Ui6-IOHnbN)M#TpS%A1Qg)Rjo zc+YHujqTZAYKBG3>J`+@oJK{)RzLTt;NTNSan;?i!Knb)Ks@NIrqYu7>H7#5tV}}c zq!fd0uq-cP+T_V3{Z)810-tg9s}RFJok%IXEsz85nlLFUg%2`x&LG0K3(_M@4Lh+U z$-XZM8DtGJo1LDlj5|)E24P1;4!<AhphCRZRYdUos?ru8l!ttcz`9jgH4vwrGMdG# z7fO^u*7X#{UdzTQ<V}**@@L9*j>H;Y*L3adJ@LBG0+8}uj0#iW(Y-8wV_FMY$Bc%1 zOHjR`PxN41byooDOO;QwS?~0PHVrJ0#L#q(+qmHI=M*9LzNFjt&o-Z8#7@}7sRVHe z`L?~w5)tl(iKD~yG{r8+aZjM@Q5K=kKwatW2;rxe@W*h%(3y0v4-!FkdM!7?VPKcD zI^?OqX@<8&pOa`SPuF*$2Z8f)*))nJigVa4H8<n9*`(gsZw0AW?G7AU9kM*hU?8wo zqv=C6P@6*4GM{Pwj7&IZVcq$}ykVx_ACBz<cVKG+mGlxl%C=zXz0xu#s7=E*SM(in z6@Durx*U2NV#aldHD45Kl(QXtcr00EYnGJe=;xx?YOQKbeTN*#3BQRI%e33Ar?AoH zKvLgePiG&OvxQ;TgtJv%UTa0pYrH`EX{oO-zIre)zZ+V~yjj{JyR-osNhcflMykS! zV(|mr06n%Zle4wGpmEbbU3<Ajw!<W}rwi)DFn)Jnj$k({li$LrLe*KDSs=P6MX481 zXV1ErEx6)EsqRxb47{yCm~HRIC)Fv)<DF4)tkb1l6m-1R6`0yCZo!hteAHM>^o=~g z9X?Rr`=uljJ;Y)^H>M|THAy!|P2V?)Gr3mqGkjP3`9bS?>uma2(iBX3hrkI;A-niG z-XciAVSz&sk#a&tD{0f12uGH1ytY0!PNsAcrT7m<<TnDM{`D+vzVSmahLQq%Bqex- z!pT<1bk}BOIzjQFvn+%-){I$ik#lnS47Jx$_-L~6q{pstmOPXuvpN(iWN$l=viOP+ zCVQDWQ!STJ(+4m@kyr&z+B`VEPB1Hs6K+0ID)lc;%gIg`;UP}+=_m}K3Dl0X6zdVk zSyiJE-zwvl_-fFzcl+39By9M(8#u_9Qp7w>)ffjLnhMKHETiYW0zUpx=Ck{ft306W zUGvd`G#EY86>n)4?F39?jNr%l-JWBesk16jjBfg%KkpS>Gs!3J(v%#0VeQsO2*b9* zZ((|w*t0D;b<%b?_eA%<^D`$l6>$)L9gDKV`4lB@b)?m~6gjN5_h=8k``S@sQ}N=` zbU1dCc3grt*L>^fN2+z{(><DP`c-t!E5XDMNVy(jheUP!auG>={kpRZY|M#A#Tup+ z3@W$_2?B~}x#NarABcp%om5&+=30KBtiUXOg^-}r&{T`OXP|kJ*5xk<k5Cebyr_e= zYO+vImefR_7>PP*rf}wY&+dqHMONIM`l@jf>6o!HpnFc(rV+=HN??l@Are#Gluc^m zU881`l-l!GYXg4ka}asEJ-Q=@_0G}VbqiHmVLTl#lA<&(4FPAVw%owcfQ-@%V|6R_ zmA9qBn~3X!e1i2>9+K`U?l=R!HTI8PJ86*{x6ub0udHy~Uy+IzOoQEU36B^rE)N~B z-~`cUF>GTZ36p`cD1v7@W1q_Kli_#RLV(#*@|XGdNWIPf8p7SQ_dipDO8?wr008>? zbBp&!aQiE)&C;B8fJkC{F6RG03b~*R<Yn#4)6k=(wN<sKNH7f(C%~tz^j@XaZ{~k) zkleyU$FH%KvW=DlLHt^C*qkc8idF>4;9-*kO@9_1a;st+9FjRH5sY<-Hs`W-UNALr z`YdxoV%>k?(tPJ|qcxrep~)_SAs-wV;j7+Xe^X|5Ov7&yuBy95lqjm2R!;D4VkmS& zdDLnsTz<W+Y~R4XH>84zZ*I)?NQn&dl{`iM?Yh`dk)84=hb7hhhK3ID`hHw<=R8w^ zV6)D``uH|1YidZjS+jl4XZv2;kxfTcQw6$|3-uL~y5j+_u(`C5hT`O%Z(2(>R5)a5 zjt%b#m$na_^;?L5)8WbFA2yI2P9$|4?Opm1uItlF29~DGDsmJmv)p7Z6d6Vht_4-c zO{;+j-G~Vo`Yk$d+_XJDF>8I0IZ1U@Z1-}*b4I5k&<$HI3rYB%>{%F|Q~0SM|2Ai; zMgkeNu`DNOFWbr;SpD7<DPmE{OQnQFi;qGZ2#Aw$s<=NFf{>yjx@na@R_-pqS!vZ@ zRVf{WYpp0*=~sEt=XeXG?!Usdp2;#0kfZT3pV`TZiD~jDlz8iNsJ`4urzW{Kg|T?h z5ih@_&12s-olD6!MAnoIgHqIe(MSn0G(AT6pxste4j*lb4r>lr9HFef{pMUJ$?0qd z%cnRu{pdz6uy88_XqWClYs<NB(L19W*M5ZfifpqUTer3Tgvzp_e)h{-ZUdlq&_+xd zyQmGa%0-to!jBFDFYobhPcU>I=^r51>3aCE4$-*@(prDi#&~Ib0bC=UKd7PF+|hw0 z&BA!;!sK=xa^@OPQZf~$4u2g2ZlE4hx9Vc1_Uyz|1Hb&rbMc~@-qRaFC65_DWm_T6 zA{R|~y>?PEYd56RFuXO?sv<J=*snbgd9@iOrPj=o+8!xEV!4(PwJTfkP6^JMjvRrp zMIIaY*a!zp(9@I4$B@+=HndL?-TGa2zdotKf~s3qt#}5X`qS@)D+m57unerqw9&PG zak|V6hroBbws;F@?MEtC5((>SD|%sr#tg`cb0}_`xq_a)$t!zzFH-~r^Ijxggk;*( zO-jzK{)Ach3G+z#S#)jC_o_A}XOiF>WCi?)CzbfMxc={_q~Q8OqY5PCyqn6kV3zJ& z<zQMoz5s}WiA_3ThX`8I7`<EI2w%#=A6(^*SOy2>0!f0q%sijiQ4GZtwdi(rB4GO) zb$)Mnc6mxR#Fi}~7anZU(%!{A4mfEd-im3Qlmw436S!6rw~BGY%Tt4^gF8gQ%o@z3 zCN)Ygq@SwZMW+?D@H2?ZHRJkYV2PgFt#tjcR^Rn<^{5xO2d1g%&?waxYb<GYwxe_+ z90Yoz9o<ROMj-WSEjI!{YchO7@sf1phMfrV8A|tPuy_|$cQrR3zTugcG^&Sr01xpc zCQlp1Gt{)oS}H;$a6{1MT4SF_yLuzGX)SBmuOl%*o-`1Kshcs|)mAG8JNp{&A2eon zEsw7eceZ+H*@t(8R6q&4ei8;p`9jB@HKw(^MLo+%dM1@`$Hi9-8^p_m+JNhC9T2^+ z`h&=le{f;HI67ro!Lqo*6Scuojqkj0r_`5!CNgc2ZdpvSOY9L0j@A4lGh%BzA6tOA zM0&Cn-SS&j0)38Z{3)M_Lr+Cr&vR*~&i7R9lia??4U?K#V<BZH{8-HNV}$;Zw&uZ= z-;X5qT;PeZArV3Y(a!L_?#w7Sox&wgY;!+mJWc%Jw+YF+8hrMsSXtlwIayvw+169& zC_D~B)!Qgen<!y@X=Pd%3KA$XRVK)MuAOLt0nHRSWIh{{YB>Jz##v2ogTmj6AD%NW zY8hIBZ6?h2BoFUB?GX-f=UI`x_$+DJiH}F(DKb#4KfkY}QC!fLdZDzG9$X!ZXVrnR zfbPEpvQ^(p)h#!@c2^T5-2BS84HL*cM=G01a8q$xTf|BlfOMae9}up3wu|h3hUYwL zg|^14qFV5-WrOA9Cb*i7a5YBP>_^@uruLd0MbQrIGq<ZZw<8TIPTHogEQuvrn$?Bk zuo7VTlJ^8Lt)nlRcOjr=u3fOg)H~Kar-V}@wVLo~bvCQXM2wG^++W5q7yA#8rxf+T z_X)K(&_v=t12OYHI9LH_enklyj6H&8r;Tlm6di2s9NCO)9Zdcxz5XvL4fW3+G5Rv? zK)lXHgl5qmcgPzOqrhsoc>b5zg^W!BYc^=lyd*Q`n;Ok|Es;dKWe#3mxG1nPO-PK> zE-p22eG#MWQ-Vz<p*m|4D><Xr{nUZ<8No6v5a(k{rjwKVYde`b`|o>U2!ynkv@w-A zb04;_eX}3CzXNo75RmbN@UB7&8$0Jy9CtnE-llF%9jLIlPl0nVuX%BXiI^_mSLzpU z!}uDR3He}>$A&El+5&QJ#{>0}Uc{p_kSu~Dcumj*-e=xyt(#fRKC|5P3`<N~y4W9; zxgS&6u~8>gR7IArgChtlK;>)f=;ZDXvYtDV_pKFt^+Q_jyTAbeCb|Ovet%QYFhD`R zi;@3@?DLhb^arQZ(K`<?4cm3Skf`uSq<b0K&`Q5SOOY+BS3Mw+TLknvHbLRDe~o=s zwT;lKphA}j?foOC3N{2mqkp#F9>3+UJYM%fHv!lUgx!MJ@UU4y;xXC`)&?&nbx9<b zd0rK!OZZi(;lK#MJH&TTOi*+un9ym7hw%tFyyQ$?ju4+5(khd%)6<GQUgO(9@1NNi ziDUBWev{VZu%VQw!)QslPV)Wst;eyU|4Iq0xYg=wJ*o+HrL{?Xv*jUcj6fUI-AW;z zFC52JaM3rYfT|EgdyhxvR2Zfl3Rm8oG+cS6MT{REwk_$Ng*JZjqh5dBGtPI_Po#y{ zyEvO;Vv^SV@~U_XZprL)wXT+K+&@wxLzp)fwY99~)Qt32q0pl8$jX|;uj4UxHW~pd zkE4|B!G-_f$$2A<>mG<yBziT<?&!Ov)5VWXa14w3^t^?GU}Mi`JdO0JnFy?-=Do{s zJq_(3**k3TlxU|>&!FDjRb-GNA^#HrPYUp~A7VQVqlIMmGrrGfMH0H?qta%P!cT)E z^XTe}3X7PG<v#`pa&(1!lxkl<le?MEpC!)zutfaY*L;!TZH03)ODoQER2w<NnyliF z^LDI#s7%p0#{*d>kFQ6@OFs^DdSzDUZy95p)<<0t;7it<*IX_c-J<@RZMijq)e(WJ z&K&e&_&vn6gXYY^CJwJnz~G<BbG8TX?5rSj8&EnnupC&v0F%5tH_+YD-O>XLo%N>) z@QL>MxP0hEPdU)ig5D8<PLDU^S4d|31i3{PX&A^}3Lgjk#=n@j38rEy2vpiwq0)x^ zR}*JwXZJq~_mgM8x0IOHpWE)hL%4Iyz=QCV>c@dXYD!sTZ>LHD_U@*}JOuPIfgQv> zsiwB&ge2B9;iLPNBt2=@SHUq~cq>_V+386<s6K@17;nkt7A_L5)ZIFqX&MK&s4w|; ziIB1f^Zq#Uo9XywNE^yzgqDUnjP1nC6l-1@P-!q-p&T>?YfpT^F;>NWiQR7*n!4tc z>J{Tkd5W*5J`eenNUF~Nqr@D)za>Gh*Ql#SE8eFizWUWiW;gjU&&ijT-k0as5N`G? z#n<Pqz^%BH(2%?uGri30)g^X?@d@vCZmcoxT7T(Ov>g;fjSWOAS*^3mongiIn&pCS zA1A42h1*~%u=&N%QXjjRL1NgQbu)4gS20<#Ki>QZMb3<w<7P5?Aqb4zFVc&^Ak}$> ziHM7VmXMN77qso(=p%XGqBmG35s31(ZTi`Ju7Q-3@QV$p!-mOE`z;ijuAxm@_cZ)2 zCa!?CQF*pdi&>$LkhJBjG_;-INuqg!a+{XJGM=+r7IeWSlk2;;ZuQ-^4)5kP<$6ve zCcU65^SjT6fn|Z3%6~sH_2&Zq`TUmyREjcx2l)HZjXw#0KI@=b@|S}g4+;NX&HgLl zB6KtR|E_I6#CcfZ{Dp*z@}JG{pVje)#1Ct7zlc+zh6p-{e^lrmB0MZD{X!r@|Ig?D ze}$%pfDbD&zW_O*`7P*>9@b|b0zAx!{Q@|JR(O8S=|B0gholcv3BO3s2_8=7S7PBI z%HJcwUl;&@Cm8_nkGSw5`9q)n3*eaIcLqH4@DC9lx{F^3(^S79Ja88O>O3BT{@wWh hf&>5zsQ)snKU{&L3<6Y&f2Jfc0A^4{V5j@}_J4qM;ynNW literal 0 HcmV?d00001 diff --git a/src/main/java/experiment/Experiment1.java b/src/main/java/experiment/Experiment1.java new file mode 100644 index 00000000..a6198533 --- /dev/null +++ b/src/main/java/experiment/Experiment1.java @@ -0,0 +1,232 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package experiment; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import javax.security.auth.login.Configuration; + +import kieker.analysis.AnalysisController; +import kieker.analysis.IAnalysisController; +import kieker.analysis.stage.EmptyPassOnFilter; +import kieker.analysis.stage.ObjectProducer; +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IStage; +import teetime.framework.core.Pipeline; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.NoopFilter; +import teetime.util.StatisticsUtil; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class Experiment1 { + + private static final int NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT = 5; + private static final int NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT = 50; + + private static final int NUMBER_OF_OBJECTS_TO_SEND = 10000; + + private static final int NUMBER_OF_MINIMAL_FILTERS = 50; + private static final int NUMBER_OF_MAXIMAL_FILTERS = 1000; + private static final int NUMBER_OF_FILTERS_PER_STEP = 50; + + private static final IAnalysis[] analyses = { new TeeTimeAnalysis(), new KiekerAnalysis() }; + + private static final List<Long> measuredTimes = new ArrayList<Long>(); + + public static void main(final String[] args) throws Exception { + System.setProperty("kieker.common.logging.Log", "NONE"); + + for (final IAnalysis analysis : analyses) { + for (int numberOfFilters = NUMBER_OF_MINIMAL_FILTERS; numberOfFilters <= NUMBER_OF_MAXIMAL_FILTERS; numberOfFilters += NUMBER_OF_FILTERS_PER_STEP) { + // Warmup + for (int run = 0; run < NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT; run++) { + analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND); + analysis.execute(); + } + + // Actual measurement + for (int run = 0; run < NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT; run++) { + final long tin = System.nanoTime(); + + analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND); + analysis.execute(); + + final long tout = System.nanoTime(); + Experiment1.addMeasuredTime((tout - tin)); + } + + Experiment1.writeAndClearMeasuredTime(analysis.getName(), numberOfFilters); + } + } + } + + private static void addMeasuredTime(final long time) { + measuredTimes.add(new Long(time)); + } + + private static void writeAndClearMeasuredTime(final String analysisName, final int numberOfFilters) throws IOException { + final FileWriter fileWriter = new FileWriter(analysisName + ".csv", true); + fileWriter.write(Integer.toString(numberOfFilters)); + fileWriter.write(";"); + + final Map<Double, Long> quintiles = StatisticsUtil.calculateQuintiles(measuredTimes); + for (final Long value : quintiles.values()) { + fileWriter.write(Long.toString(value)); + fileWriter.write(";"); + } + + fileWriter.write(Long.toString(StatisticsUtil.calculateAverage(measuredTimes))); + fileWriter.write(";"); + + fileWriter.write(Long.toString(StatisticsUtil.calculateConfidenceWidth(measuredTimes))); + + fileWriter.write("\n"); + fileWriter.close(); + + measuredTimes.clear(); + } + + private static interface IAnalysis { + + public void initialize(int numberOfFilters, int numberOfObjectsToSend) throws Exception; + + public String getName(); + + public void execute() throws Exception; + + } + + private static final class TeeTimeAnalysis extends Analysis implements IAnalysis { + + private static final int SECONDS = 1000; + + private Pipeline pipeline; + private WorkerThread workerThread; + + public TeeTimeAnalysis() {} + + @Override + public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) { + + @SuppressWarnings("unchecked") + final NoopFilter<Object>[] noopFilters = new NoopFilter[numberOfFilters]; + // create stages + final teetime.stage.basic.ObjectProducer<Object> objectProducer = new teetime.stage.basic.ObjectProducer<Object>( + numberOfObjectsToSend, new Callable<Object>() { + @Override + public Object call() throws Exception { + return new Object(); + } + }); + for (int i = 0; i < noopFilters.length; i++) { + noopFilters[i] = new NoopFilter<Object>(); + } + + // add each stage to a stage list + final List<IStage> startStages = new LinkedList<IStage>(); + startStages.add(objectProducer); + + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(objectProducer); + stages.addAll(Arrays.asList(noopFilters)); + + // connect stages by pipes + QueuePipe.connect(objectProducer.outputPort, noopFilters[0].inputPort); + for (int i = 1; i < noopFilters.length; i++) { + QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort); + } + + this.pipeline = new Pipeline(); + this.pipeline.setStartStages(startStages); + this.pipeline.setStages(stages); + + this.workerThread = new WorkerThread(this.pipeline, 0); + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + } + + @Override + public String getName() { + return "TeeTime"; + } + + @Override + public void execute() { + super.start(); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + } + + private static final class KiekerAnalysis implements IAnalysis { + + private IAnalysisController ac; + + public KiekerAnalysis() {} + + @Override + public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) throws Exception { + this.ac = new AnalysisController(); + + final Configuration producerConfig = new Configuration(); + producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(numberOfObjectsToSend)); + final ObjectProducer<Object> producer = new ObjectProducer<Object>(producerConfig, this.ac, new Callable<Object>() { + @Override + public Object call() throws Exception { + return new Object(); + } + }); + + EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + for (int idx = 0; idx < (numberOfFilters - 1); idx++) { + final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + predecessor = newPredecessor; + } + } + + @Override + public String getName() { + return "Kieker"; + } + + @Override + public void execute() throws Exception { + this.ac.run(); + } + + } + +} diff --git a/src/main/java/experiment/Experiment2.java b/src/main/java/experiment/Experiment2.java new file mode 100644 index 00000000..2c261806 --- /dev/null +++ b/src/main/java/experiment/Experiment2.java @@ -0,0 +1,263 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package experiment; + +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Callable; + +import javax.security.auth.login.Configuration; + +import kieker.analysis.AnalysisController; +import kieker.analysis.IAnalysisController; +import kieker.analysis.stage.CollectorSink; +import kieker.analysis.stage.EmptyPassOnFilter; +import kieker.analysis.stage.ObjectProducer; +import kieker.analysis.stage.StartTimestampFilter; +import kieker.analysis.stage.StopTimestampFilter; +import teetime.examples.throughput.TimestampObject; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IStage; +import teetime.framework.core.Pipeline; +import teetime.framework.sequential.MethodCallPipe; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.NoopFilter; +import teetime.util.StatisticsUtil; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class Experiment2 { + + private static final int NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT = 5; + private static final int NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT = 50; + + private static final int NUMBER_OF_OBJECTS_TO_SEND = 10000; + + private static final int NUMBER_OF_MINIMAL_FILTERS = 50; + private static final int NUMBER_OF_MAXIMAL_FILTERS = 1000; + private static final int NUMBER_OF_FILTERS_PER_STEP = 50; + + private static final IAnalysis[] analyses = { new TeeTimeAnalysis(true), new TeeTimeAnalysis(false), new KiekerAnalysis() }; + + private static final List<Long> measuredTimes = new ArrayList<Long>(); + + public static void main(final String[] args) throws Exception { + System.setProperty("kieker.common.logging.Log", "NONE"); + + for (final IAnalysis analysis : analyses) { + for (int numberOfFilters = NUMBER_OF_MINIMAL_FILTERS; numberOfFilters <= NUMBER_OF_MAXIMAL_FILTERS; numberOfFilters += NUMBER_OF_FILTERS_PER_STEP) { + // Warmup + for (int run = 0; run < NUMBER_OF_WARMUP_RUNS_PER_EXPERIMENT; run++) { + analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND); + analysis.execute(); + } + + // Actual measurement + for (int run = 0; run < NUMBER_OF_MEASURED_RUNS_PER_EXPERIMENT; run++) { + final long tin = System.nanoTime(); + + analysis.initialize(numberOfFilters, NUMBER_OF_OBJECTS_TO_SEND); + analysis.execute(); + + final long tout = System.nanoTime(); + Experiment2.addMeasuredTime((tout - tin)); + } + + Experiment2.writeAndClearMeasuredTime(analysis.getName(), numberOfFilters); + } + } + } + + private static void addMeasuredTime(final long time) { + measuredTimes.add(new Long(time)); + } + + private static void writeAndClearMeasuredTime(final String analysisName, final int numberOfFilters) throws IOException { + final FileWriter fileWriter = new FileWriter(analysisName + ".csv", true); + fileWriter.write(Integer.toString(numberOfFilters)); + fileWriter.write(";"); + + final Map<Double, Long> quintiles = StatisticsUtil.calculateQuintiles(measuredTimes); + for (final Long value : quintiles.values()) { + fileWriter.write(Long.toString(value)); + fileWriter.write(";"); + } + + fileWriter.write(Long.toString(StatisticsUtil.calculateAverage(measuredTimes))); + fileWriter.write(";"); + + fileWriter.write(Long.toString(StatisticsUtil.calculateConfidenceWidth(measuredTimes))); + + fileWriter.write("\n"); + fileWriter.close(); + + measuredTimes.clear(); + } + + private static interface IAnalysis { + + public String getName(); + + public void execute() throws Exception; + + public void initialize(int numberOfFilters, int numberOfObjectsToSend) throws Exception; + + } + + private static final class TeeTimeAnalysis extends Analysis implements IAnalysis { + + private static final int SECONDS = 1000; + + private Pipeline pipeline; + private WorkerThread workerThread; + private final boolean shouldUseQueue; + + public TeeTimeAnalysis(final boolean shouldUseQueue) { + this.shouldUseQueue = shouldUseQueue; + } + + @Override + public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) throws Exception { + @SuppressWarnings("unchecked") + final NoopFilter<TimestampObject>[] noopFilters = new NoopFilter[numberOfFilters]; + // create stages + final teetime.stage.basic.ObjectProducer<TimestampObject> objectProducer = new teetime.stage.basic.ObjectProducer<TimestampObject>( + numberOfObjectsToSend, new Callable<TimestampObject>() { + @Override + public TimestampObject call() throws Exception { + return new TimestampObject(); + } + }); + final teetime.stage.StartTimestampFilter startTimestampFilter = new teetime.stage.StartTimestampFilter(); + for (int i = 0; i < noopFilters.length; i++) { + noopFilters[i] = new NoopFilter<TimestampObject>(); + } + final teetime.stage.StopTimestampFilter stopTimestampFilter = new teetime.stage.StopTimestampFilter(); + final teetime.stage.CollectorSink<TimestampObject> collectorSink = new teetime.stage.CollectorSink<TimestampObject>( + this.timestampObjects); + + // add each stage to a stage list + final List<IStage> startStages = new LinkedList<IStage>(); + startStages.add(objectProducer); + + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(objectProducer); + if (this.shouldUseQueue) { + stages.add(startTimestampFilter); + stages.addAll(Arrays.asList(noopFilters)); + stages.add(stopTimestampFilter); + stages.add(collectorSink); + + // connect stages by pipes + QueuePipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort); + QueuePipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort); + for (int i = 1; i < noopFilters.length; i++) { + QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort); + } + QueuePipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort); + QueuePipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort); + } else { + // connect stages by pipes + MethodCallPipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort); + MethodCallPipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort); + for (int i = 1; i < noopFilters.length; i++) { + MethodCallPipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort); + } + MethodCallPipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort); + MethodCallPipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort); + } + + this.pipeline = new Pipeline(); + this.pipeline.setStartStages(startStages); + this.pipeline.setStages(stages); + } + + @Override + public String getName() { + return "TeeTime" + (this.shouldUseQueue ? "-Queues" : "-NoQueues"); + } + + @Override + public void execute() { + super.start(); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + } + + private static final class KiekerAnalysis implements IAnalysis { + + private IAnalysisController ac; + + public KiekerAnalysis() {} + + @Override + public void initialize(final int numberOfFilters, final int numberOfObjectsToSend) throws Exception { + this.ac = new AnalysisController(); + + final Configuration producerConfig = new Configuration(); + producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(numberOfObjectsToSend)); + final ObjectProducer<TimestampObject> producer = new ObjectProducer<TimestampObject>(producerConfig, this.ac, new Callable<TimestampObject>() { + @Override + public TimestampObject call() throws Exception { + return new TimestampObject(); + } + }); + + final StartTimestampFilter startTimestampFilter = new StartTimestampFilter(new Configuration(), this.ac); + EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, startTimestampFilter, StartTimestampFilter.INPUT_PORT_NAME); + this.ac.connect(startTimestampFilter, StartTimestampFilter.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + for (int idx = 0; idx < (numberOfFilters - 1); idx++) { + final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + predecessor = newPredecessor; + } + final StopTimestampFilter stopTimestampFilter = new StopTimestampFilter(new Configuration(), this.ac); + final CollectorSink<TimestampObject> collectorSink = new CollectorSink<TimestampObject>(new Configuration(), this.ac, this.timestampObjects); + + this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, stopTimestampFilter, StopTimestampFilter.INPUT_PORT_NAME); + this.ac.connect(stopTimestampFilter, StopTimestampFilter.OUTPUT_PORT_NAME, collectorSink, CollectorSink.INPUT_PORT_NAME); + } + + @Override + public String getName() { + return "Kieker"; + } + + @Override + public void execute() throws Exception { + this.ac.run(); + } + + } + +} diff --git a/src/main/java/kieker/analysis/examples/ThroughputAnalysis.java b/src/main/java/kieker/analysis/examples/ThroughputAnalysis.java new file mode 100644 index 00000000..589185ab --- /dev/null +++ b/src/main/java/kieker/analysis/examples/ThroughputAnalysis.java @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.examples; + +import java.util.concurrent.Callable; + +import kieker.analysis.AnalysisController; +import kieker.analysis.IAnalysisController; +import kieker.analysis.exception.AnalysisConfigurationException; +import kieker.analysis.stage.EmptyPassOnFilter; +import kieker.analysis.stage.ObjectProducer; +import kieker.common.configuration.Configuration; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class ThroughputAnalysis<T> { + + private final IAnalysisController ac = new AnalysisController(); + private int numNoopFilters; + private int numInputObjects; + private Callable<T> inputObjectCreator; + + public void setNumNoopFilters(final int numNoopFilters) { + this.numNoopFilters = numNoopFilters; + } + + public void setInput(final int numInputObjects, final Callable<T> inputObjectCreator) { + this.numInputObjects = numInputObjects; + this.inputObjectCreator = inputObjectCreator; + } + + public void start() throws IllegalStateException, AnalysisConfigurationException { + this.ac.run(); + } + + public void init() throws IllegalStateException, AnalysisConfigurationException { + final Configuration producerConfig = new Configuration(); + producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(this.numInputObjects)); + final ObjectProducer<T> producer = new ObjectProducer<T>(producerConfig, this.ac, this.inputObjectCreator); + + EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + for (int idx = 0; idx < (this.numNoopFilters - 1); idx++) { + final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + predecessor = newPredecessor; + } + } + +} diff --git a/src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java b/src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java new file mode 100644 index 00000000..483957ad --- /dev/null +++ b/src/main/java/kieker/analysis/examples/ThroughputTimestampAnalysis.java @@ -0,0 +1,83 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.examples; + +import java.util.Collection; +import java.util.concurrent.Callable; + +import kieker.analysis.AnalysisController; +import kieker.analysis.IAnalysisController; +import kieker.analysis.exception.AnalysisConfigurationException; +import kieker.analysis.stage.CollectorSink; +import kieker.analysis.stage.EmptyPassOnFilter; +import kieker.analysis.stage.ObjectProducer; +import kieker.analysis.stage.StartTimestampFilter; +import kieker.analysis.stage.StopTimestampFilter; +import kieker.common.configuration.Configuration; +import teetime.examples.throughput.TimestampObject; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class ThroughputTimestampAnalysis { + + private final IAnalysisController ac = new AnalysisController(); + private int numNoopFilters; + private int numInputObjects; + private Callable<TimestampObject> inputObjectCreator; + private Collection<TimestampObject> timestampObjects; + + public void setNumNoopFilters(final int numNoopFilters) { + this.numNoopFilters = numNoopFilters; + } + + public void setInput(final int numInputObjects, final Callable<TimestampObject> inputObjectCreator) { + this.numInputObjects = numInputObjects; + this.inputObjectCreator = inputObjectCreator; + } + + public void setTimestampObjects(final Collection<TimestampObject> timestampObjects) { + this.timestampObjects = timestampObjects; + } + + public void start() throws IllegalStateException, AnalysisConfigurationException { + this.ac.run(); + } + + public void init() throws IllegalStateException, AnalysisConfigurationException { + final Configuration producerConfig = new Configuration(); + producerConfig.setProperty(ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(this.numInputObjects)); + final ObjectProducer<TimestampObject> producer = new ObjectProducer<TimestampObject>(producerConfig, this.ac, this.inputObjectCreator); + + final StartTimestampFilter startTimestampFilter = new StartTimestampFilter(new Configuration(), this.ac); + EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(producer, ObjectProducer.OUTPUT_PORT_NAME, startTimestampFilter, StartTimestampFilter.INPUT_PORT_NAME); + this.ac.connect(startTimestampFilter, StartTimestampFilter.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + for (int idx = 0; idx < (this.numNoopFilters - 1); idx++) { + final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), this.ac); + this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + predecessor = newPredecessor; + } + final StopTimestampFilter stopTimestampFilter = new StopTimestampFilter(new Configuration(), this.ac); + final CollectorSink<TimestampObject> collectorSink = new CollectorSink<TimestampObject>(new Configuration(), this.ac, this.timestampObjects); + + this.ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, stopTimestampFilter, StopTimestampFilter.INPUT_PORT_NAME); + this.ac.connect(stopTimestampFilter, StopTimestampFilter.OUTPUT_PORT_NAME, collectorSink, CollectorSink.INPUT_PORT_NAME); + } + +} diff --git a/src/main/java/kieker/analysis/stage/CacheFilter.java b/src/main/java/kieker/analysis/stage/CacheFilter.java new file mode 100644 index 00000000..9d9d94b1 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/CacheFilter.java @@ -0,0 +1,64 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import java.util.ArrayList; +import java.util.List; + +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.InputPort; +import kieker.analysis.plugin.annotation.OutputPort; +import kieker.analysis.plugin.annotation.Plugin; +import kieker.analysis.plugin.filter.AbstractFilterPlugin; +import kieker.common.configuration.Configuration; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +@Plugin(outputPorts = @OutputPort(name = CacheFilter.OUTPUT_PORT_NAME)) +public class CacheFilter extends AbstractFilterPlugin { + + public static final String INPUT_PORT_NAME = "input"; + public static final String OUTPUT_PORT_NAME = "output"; + + private final List<Object> cache = new ArrayList<Object>(); + + public CacheFilter(final Configuration configuration, + final IProjectContext projectContext) { + super(configuration, projectContext); + } + + @Override + public void terminate(final boolean error) { + for (final Object data : this.cache) { + super.deliver(EmptyPassOnFilter.OUTPUT_PORT_NAME, data); + } + super.terminate(error); + } + + @Override + public Configuration getCurrentConfiguration() { + return new Configuration(); + } + + @InputPort(name = CacheFilter.INPUT_PORT_NAME) + public void input(final Object data) { + this.cache.add(data); + } + +} diff --git a/src/main/java/kieker/analysis/stage/CollectorSink.java b/src/main/java/kieker/analysis/stage/CollectorSink.java new file mode 100644 index 00000000..279c4716 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/CollectorSink.java @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import java.util.Collection; + +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.InputPort; +import kieker.analysis.plugin.filter.AbstractFilterPlugin; +import kieker.common.configuration.Configuration; + +public class CollectorSink<T> extends AbstractFilterPlugin { + + public static final String INPUT_PORT_NAME = "input"; + + private static final int THRESHOLD = 10000; + + private Collection<T> objects; + + public CollectorSink(final Configuration configuration, final IProjectContext projectContext, final Collection<T> collection) { + super(configuration, projectContext); + this.objects = collection; + } + + @InputPort(name = CollectorSink.INPUT_PORT_NAME) + public void execute(final T object) { + this.objects.add(object); + if ((this.objects.size() % THRESHOLD) == 0) { + System.out.println("size: " + this.objects.size()); + } + } + + public Collection<T> getObjects() { + return this.objects; + } + + public void setObjects(final Collection<T> objects) { + this.objects = objects; + } + + @Override + public Configuration getCurrentConfiguration() { + return new Configuration(); + } + +} diff --git a/src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java b/src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java new file mode 100644 index 00000000..f5350349 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/EmptyPassOnFilter.java @@ -0,0 +1,51 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.InputPort; +import kieker.analysis.plugin.annotation.OutputPort; +import kieker.analysis.plugin.annotation.Plugin; +import kieker.analysis.plugin.filter.AbstractFilterPlugin; +import kieker.common.configuration.Configuration; + + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +@Plugin(outputPorts = @OutputPort(name = EmptyPassOnFilter.OUTPUT_PORT_NAME)) +public class EmptyPassOnFilter extends AbstractFilterPlugin { + + public static final String INPUT_PORT_NAME = "input"; + public static final String OUTPUT_PORT_NAME = "output"; + + public EmptyPassOnFilter(final Configuration configuration, final IProjectContext projectContext) { + super(configuration, projectContext); + } + + @Override + public Configuration getCurrentConfiguration() { + return new Configuration(); + } + + @InputPort(name = EmptyPassOnFilter.INPUT_PORT_NAME) + public void input(final Object data) { + super.deliver(EmptyPassOnFilter.OUTPUT_PORT_NAME, data); + } + +} diff --git a/src/main/java/kieker/analysis/stage/MappingFileParser.java b/src/main/java/kieker/analysis/stage/MappingFileParser.java new file mode 100644 index 00000000..149c93f8 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/MappingFileParser.java @@ -0,0 +1,129 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.HashMap; +import java.util.Map; + +import kieker.common.logging.Log; +import kieker.common.util.filesystem.FSUtil; +import teetime.stage.kieker.className.ClassNameRegistry; + + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class MappingFileParser { + + protected Log logger; + + private static final Map<String, String> filePrefixRegistry = new HashMap<String, String>(); + + static { + filePrefixRegistry.put(FSUtil.MAP_FILENAME, FSUtil.FILE_PREFIX); + filePrefixRegistry.put(FSUtil.LEGACY_MAP_FILENAME, FSUtil.LEGACY_FILE_PREFIX); + } + + public MappingFileParser(final Log logger) { + this.logger = logger; + } + + public ClassNameRegistry parseFromStream(final InputStream inputStream) { + final ClassNameRegistry classNameRegistry = new ClassNameRegistry(); + + BufferedReader in = null; + try { + in = new BufferedReader(new InputStreamReader(inputStream, FSUtil.ENCODING)); + String line; + while ((line = in.readLine()) != null) { // NOPMD (assign) + this.parseTextLine(line, classNameRegistry); + } + } catch (final IOException ex) { + this.logger.error("Error reading mapping file", ex); + } finally { + if (in != null) { + try { + in.close(); + } catch (final IOException ex) { + this.logger.error("Exception while closing input stream for mapping file", ex); + } + } + } + + return classNameRegistry; + } + + private void parseTextLine(final String line, final Map<Integer, String> stringRegistry) { + if (line.length() == 0) { + return; // ignore empty lines + } + final int split = line.indexOf('='); + if (split == -1) { + this.logger.error("Failed to find character '=' in line: {" + line + "}. It must consist of a ID=VALUE pair."); + return; // continue on errors + } + final String key = line.substring(0, split); + final String value = FSUtil.decodeNewline(line.substring(split + 1)); + // the leading $ is optional + final Integer id; + try { + id = Integer.valueOf((key.charAt(0) == '$') ? key.substring(1) : key); // NOCS + } catch (final NumberFormatException ex) { + this.logger.error("Error reading mapping file, id must be integer", ex); + return; // continue on errors + } + final String prevVal = stringRegistry.put(id, value); + if (prevVal != null) { + this.logger.error("Found addional entry for id='" + id + "', old value was '" + prevVal + "' new value is '" + value + "'"); + } + } + + /** + * @since 1.10 + */ + public File findMappingFile(final File dirPath) { + File mappingFile = new File(dirPath, FSUtil.MAP_FILENAME); + if (!mappingFile.exists()) { + // No mapping file found. Check whether we find a legacy tpmon.map file! + mappingFile = new File(dirPath, FSUtil.LEGACY_MAP_FILENAME); + if (mappingFile.exists()) { + this.logger.info("Directory '" + dirPath + "' contains no file '" + FSUtil.MAP_FILENAME + "'. Found '" + FSUtil.LEGACY_MAP_FILENAME + + "' ... switching to legacy mode"); + } else { + // no {kieker|tpmon}.map exists. This is valid for very old monitoring logs. Hence, only dump a log.warn + this.logger.warn("No mapping file in directory '" + dirPath.getAbsolutePath() + "'"); + return null; + } + } + + return mappingFile; + } + + /** + * @return <code>null</code> if a file prefix for the given <code>mappingFile</code> is not registered. + * @since 1.10 + */ + public String getFilePrefixFromMappingFile(final File mappingFile) { + return MappingFileParser.filePrefixRegistry.get(mappingFile.getName()); + } +} diff --git a/src/main/java/kieker/analysis/stage/ObjectProducer.java b/src/main/java/kieker/analysis/stage/ObjectProducer.java new file mode 100644 index 00000000..515ae0fc --- /dev/null +++ b/src/main/java/kieker/analysis/stage/ObjectProducer.java @@ -0,0 +1,88 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import java.util.concurrent.Callable; + +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.OutputPort; +import kieker.analysis.plugin.annotation.Plugin; +import kieker.analysis.plugin.annotation.Property; +import kieker.analysis.plugin.reader.AbstractReaderPlugin; +import kieker.common.configuration.Configuration; + + +@Plugin( + outputPorts = @OutputPort(name = ObjectProducer.OUTPUT_PORT_NAME), + configuration = @Property(name = ObjectProducer.CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, defaultValue = "100")) +public class ObjectProducer<T> extends AbstractReaderPlugin { + + public static final String CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE = "numObjectsToCreate"; + public static final String OUTPUT_PORT_NAME = "output"; + + private Callable<T> objectCreator; + private long numObjectsToCreate; + + public ObjectProducer(final Configuration configuration, final IProjectContext projectContext, final Callable<T> objectCreator) { + super(configuration, projectContext); + + this.numObjectsToCreate = configuration.getLongProperty(CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE); + this.objectCreator = objectCreator; + } + + public Callable<T> getObjectCreator() { + return this.objectCreator; + } + + public void setObjectCreator(final Callable<T> objectCreator) { + this.objectCreator = objectCreator; + } + + public long getNumObjectsToCreate() { + return this.numObjectsToCreate; + } + + public void setNumObjectsToCreate(final long numObjectsToCreate) { + this.numObjectsToCreate = numObjectsToCreate; + } + + @Override + public boolean read() { + for (int idx = 0; idx < this.numObjectsToCreate; idx++) { + try { + final T newObject = this.objectCreator.call(); + super.deliver(OUTPUT_PORT_NAME, newObject); + } catch (final Exception e) { + throw new IllegalStateException(); + } + } + + return true; + } + + @Override + public void terminate(final boolean error) { + // Nothing to do + } + + @Override + public Configuration getCurrentConfiguration() { + final Configuration configuration = new Configuration(); + configuration.setProperty(CONFIG_PROPERTY_NAME_OBJECTS_TO_CREATE, Long.toString(this.numObjectsToCreate)); + return configuration; + } + +} diff --git a/src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java b/src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java new file mode 100644 index 00000000..2889e932 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/RecordFromBinaryFileCreator.java @@ -0,0 +1,106 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.File; +import java.io.IOException; + +import kieker.common.exception.MonitoringRecordException; +import kieker.common.logging.Log; +import kieker.common.record.AbstractMonitoringRecord; +import kieker.common.record.IMonitoringRecord; +import teetime.stage.kieker.className.ClassNameRegistry; +import teetime.stage.kieker.className.ClassNameRegistryRepository; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class RecordFromBinaryFileCreator { + + private final Log logger; + private final ClassNameRegistryRepository classNameRegistryRepository; + + public RecordFromBinaryFileCreator(final Log logger, final ClassNameRegistryRepository classNameRegistryRepository) { + this.logger = logger; + this.classNameRegistryRepository = classNameRegistryRepository; + } + + public IMonitoringRecord createRecordFromBinaryFile(final File binaryFile, final DataInputStream inputStream) throws IOException, MonitoringRecordException { + final ClassNameRegistry classNameRegistry = this.classNameRegistryRepository.get(binaryFile.getParentFile()); + + final Integer id; + try { + id = inputStream.readInt(); + } catch (final EOFException eof) { + return null; // we are finished + } + final String classname = classNameRegistry.get(id); + if (classname == null) { + this.logger.error("Missing classname mapping for record type id " + "'" + id + "'"); + return null; // we can't easily recover on errors + } + + final Class<? extends IMonitoringRecord> clazz = AbstractMonitoringRecord.classForName(classname); + final Class<?>[] typeArray = AbstractMonitoringRecord.typesForClass(clazz); + + // read record + final long loggingTimestamp = inputStream.readLong(); // NOPMD (must be read here!) + final Object[] objectArray = new Object[typeArray.length]; + int idx = -1; + for (final Class<?> type : typeArray) { + idx++; + if (type == String.class) { + final Integer strId = inputStream.readInt(); + final String str = classNameRegistry.get(strId); + if (str == null) { + this.logger.error("No String mapping found for id " + strId.toString()); + objectArray[idx] = ""; + } else { + objectArray[idx] = str; + } + } else if ((type == int.class) || (type == Integer.class)) { + objectArray[idx] = inputStream.readInt(); + } else if ((type == long.class) || (type == Long.class)) { + objectArray[idx] = inputStream.readLong(); + } else if ((type == float.class) || (type == Float.class)) { + objectArray[idx] = inputStream.readFloat(); + } else if ((type == double.class) || (type == Double.class)) { + objectArray[idx] = inputStream.readDouble(); + } else if ((type == byte.class) || (type == Byte.class)) { + objectArray[idx] = inputStream.readByte(); + } else if ((type == short.class) || (type == Short.class)) { // NOPMD (short) + objectArray[idx] = inputStream.readShort(); + } else if ((type == boolean.class) || (type == Boolean.class)) { + objectArray[idx] = inputStream.readBoolean(); + } else { + if (inputStream.readByte() != 0) { + this.logger.error("Unexpected value for unsupported type: " + clazz.getName()); + return null; // breaking error (break would not terminate the correct loop) + } + this.logger.warn("Unsupported type: " + clazz.getName()); + objectArray[idx] = null; + } + } + final IMonitoringRecord record = AbstractMonitoringRecord.createFromArray(clazz, objectArray); + record.setLoggingTimestamp(loggingTimestamp); + + return record; + } +} diff --git a/src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java b/src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java new file mode 100644 index 00000000..50a9db46 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/RecordFromTextLineCreator.java @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import java.io.File; + +import kieker.common.exception.MonitoringRecordException; +import kieker.common.record.AbstractMonitoringRecord; +import kieker.common.record.IMonitoringRecord; +import kieker.common.record.controlflow.OperationExecutionRecord; +import teetime.stage.MappingException; +import teetime.stage.kieker.className.ClassNameRegistry; +import teetime.stage.kieker.className.ClassNameRegistryRepository; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class RecordFromTextLineCreator { + + private static final String CSV_SEPARATOR_CHARACTER = ";"; + + private static final IllegalRecordFormatException ILLEGAL_RECORD_FORMAT_EXCEPTION = new IllegalRecordFormatException(); + + private final ClassNameRegistryRepository classNameRegistryRepository; + + public RecordFromTextLineCreator(final ClassNameRegistryRepository classNameRegistryRepository) { + this.classNameRegistryRepository = classNameRegistryRepository; + } + + /** + * @since 1.10 + */ + public IMonitoringRecord createRecordFromLine(final File textFile, final String line) throws MonitoringRecordException, IllegalRecordFormatException, + MappingException, + UnknownRecordTypeException { + final String[] recordFields = line.split(CSV_SEPARATOR_CHARACTER); + + if (recordFields.length < 2) { + throw ILLEGAL_RECORD_FORMAT_EXCEPTION; + } + + final boolean isModernRecord = recordFields[0].charAt(0) == '$'; + if (isModernRecord) { + return this.createModernRecordFromRecordFields(textFile, recordFields); + } else { + return this.createLegacyRecordFromRecordFiels(recordFields); + } + } + + private IMonitoringRecord createModernRecordFromRecordFields(final File textFile, final String[] recordFields) throws MonitoringRecordException, + MappingException, + UnknownRecordTypeException { + final ClassNameRegistry classNameRegistry = this.classNameRegistryRepository.get(textFile.getParentFile()); + final Integer id = Integer.valueOf(recordFields[0].substring(1)); + final String classname = classNameRegistry.get(id); + if (classname == null) { + throw new MappingException("Missing classname mapping for record type id " + "'" + id + "'"); + } + final Class<? extends IMonitoringRecord> clazz = this.getClassByName(classname); + final long loggingTimestamp = Long.parseLong(recordFields[1]); + final int skipValues; + // check for Kieker < 1.6 OperationExecutionRecords + if ((recordFields.length == 11) && clazz.equals(OperationExecutionRecord.class)) { + skipValues = 3; + } else { + skipValues = 2; + } + // Java 1.5 compatibility + final String[] recordFieldsReduced = new String[recordFields.length - skipValues]; + System.arraycopy(recordFields, skipValues, recordFieldsReduced, 0, recordFields.length - skipValues); + // in Java 1.6 this could be simplified to + // recordFieldsReduced = Arrays.copyOfRange(recordFields, skipValues, recordFields.length); + + final IMonitoringRecord record = AbstractMonitoringRecord.createFromStringArray(clazz, recordFieldsReduced); + record.setLoggingTimestamp(loggingTimestamp); + return record; + } + + /** + * @since 1.10 + */ + private Class<? extends IMonitoringRecord> getClassByName(final String classname) throws MonitoringRecordException, UnknownRecordTypeException { + try { + return AbstractMonitoringRecord.classForName(classname); + } catch (final MonitoringRecordException ex) { + throw new UnknownRecordTypeException("Failed to load record type " + classname, classname, ex); + } + } + + /** + * @since 1.10 + */ + private IMonitoringRecord createLegacyRecordFromRecordFiels(final String[] recordFields) throws MonitoringRecordException { + final String[] recordFieldsReduced = new String[recordFields.length - 1]; + System.arraycopy(recordFields, 1, recordFieldsReduced, 0, recordFields.length - 1); + return AbstractMonitoringRecord.createFromStringArray(OperationExecutionRecord.class, recordFieldsReduced); + } +} diff --git a/src/main/java/kieker/analysis/stage/StartTimestampFilter.java b/src/main/java/kieker/analysis/stage/StartTimestampFilter.java new file mode 100644 index 00000000..f3b61b05 --- /dev/null +++ b/src/main/java/kieker/analysis/stage/StartTimestampFilter.java @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.InputPort; +import kieker.analysis.plugin.annotation.OutputPort; +import kieker.analysis.plugin.annotation.Plugin; +import kieker.analysis.plugin.filter.AbstractFilterPlugin; +import kieker.common.configuration.Configuration; +import teetime.examples.throughput.TimestampObject; + +@Plugin(outputPorts = @OutputPort(name = StartTimestampFilter.OUTPUT_PORT_NAME)) +public class StartTimestampFilter extends AbstractFilterPlugin { + + public static final String INPUT_PORT_NAME = "input"; + public static final String OUTPUT_PORT_NAME = "output"; + + public StartTimestampFilter(final Configuration configuration, final IProjectContext projectContext) { + super(configuration, projectContext); + } + + @InputPort(name = StartTimestampFilter.INPUT_PORT_NAME) + public void execute(final TimestampObject inputObject) { + inputObject.setStartTimestamp(System.nanoTime()); + super.deliver(StartTimestampFilter.OUTPUT_PORT_NAME, inputObject); + } + + @Override + public Configuration getCurrentConfiguration() { + return new Configuration(); + } + +} diff --git a/src/main/java/kieker/analysis/stage/StopTimestampFilter.java b/src/main/java/kieker/analysis/stage/StopTimestampFilter.java new file mode 100644 index 00000000..eac2426d --- /dev/null +++ b/src/main/java/kieker/analysis/stage/StopTimestampFilter.java @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.InputPort; +import kieker.analysis.plugin.annotation.OutputPort; +import kieker.analysis.plugin.annotation.Plugin; +import kieker.analysis.plugin.filter.AbstractFilterPlugin; +import kieker.common.configuration.Configuration; +import teetime.examples.throughput.TimestampObject; + +@Plugin(outputPorts = @OutputPort(name = StopTimestampFilter.OUTPUT_PORT_NAME)) +public class StopTimestampFilter extends AbstractFilterPlugin { + + public static final String INPUT_PORT_NAME = "input"; + public static final String OUTPUT_PORT_NAME = "output"; + + public StopTimestampFilter(final Configuration configuration, final IProjectContext projectContext) { + super(configuration, projectContext); + } + + @InputPort(name = StopTimestampFilter.INPUT_PORT_NAME) + public void execute(final TimestampObject inputObject) { + inputObject.setStopTimestamp(System.nanoTime()); + super.deliver(StartTimestampFilter.OUTPUT_PORT_NAME, inputObject); + } + + @Override + public Configuration getCurrentConfiguration() { + return new Configuration(); + } + +} diff --git a/src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java b/src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java new file mode 100644 index 00000000..cfc813af --- /dev/null +++ b/src/main/java/teetime/examples/countWords/ConcurrentCountWordsAnalysis.java @@ -0,0 +1,341 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countWords; + +import java.io.File; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import teetime.framework.concurrent.ConcurrentWorkStealingPipe; +import teetime.framework.concurrent.ConcurrentWorkStealingPipeFactory; +import teetime.framework.concurrent.SingleProducerSingleConsumerPipe; +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IPipeline; +import teetime.framework.core.ISink; +import teetime.framework.core.ISource; +import teetime.framework.core.IStage; +import teetime.framework.sequential.MethodCallPipe; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.basic.RepeaterSource; +import teetime.stage.basic.distributor.Distributor; +import teetime.stage.basic.merger.Merger; +import teetime.util.Pair; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ConcurrentCountWordsAnalysis extends Analysis { + + private static final int NUM_TOKENS_TO_REPEAT = 1000; + private static final String START_DIRECTORY_NAME = "."; + private static final int SECONDS = 1000; + + private static final int MAX_NUM_THREADS = 3; + + private WorkerThread[] ioThreads; + private WorkerThread[] nonIoThreads; + + ConcurrentWorkStealingPipeFactory<?>[] pipeFactories; + + @Override + public void init() { + super.init(); + + this.ioThreads = new WorkerThread[2]; + + final IPipeline readerThreadPipeline = this.readerThreadPipeline(); + @SuppressWarnings("unchecked") + final Distributor<File> distributor = (Distributor<File>) readerThreadPipeline.getStages().get(readerThreadPipeline.getStages().size() - 1); + this.ioThreads[0] = new WorkerThread(readerThreadPipeline, 1); + this.ioThreads[0].setName("startThread"); + this.ioThreads[0].terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + + final IPipeline printingThreadPipeline = this.printingThreadPipeline(); + @SuppressWarnings("unchecked") + final Merger<Pair<File, Integer>> merger = (Merger<Pair<File, Integer>>) printingThreadPipeline.getStages().get(0); + this.ioThreads[1] = new WorkerThread(printingThreadPipeline, 2); + this.ioThreads[1].setName("printingThread"); + this.ioThreads[1].setTerminationPolicy(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + + this.createPipeFactories(); + + int numThreads = Runtime.getRuntime().availableProcessors(); + numThreads = Math.min(MAX_NUM_THREADS, numThreads); // only for testing purposes + + this.nonIoThreads = new WorkerThread[numThreads]; + for (int i = 0; i < this.nonIoThreads.length; i++) { + final IPipeline pipeline = this.buildNonIoPipeline(distributor, merger); + this.nonIoThreads[i] = new WorkerThread(pipeline, 0); + this.nonIoThreads[i].setTerminationPolicy(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + } + } + + @Override + public void start() { + super.start(); + + for (final WorkerThread thread : this.ioThreads) { + thread.start(); + } + + for (final WorkerThread thread : this.nonIoThreads) { + thread.start(); + } + + System.out.println("Waiting for the non I/O worker threads to terminate..."); // NOPMD (Just for example purposes) + for (final WorkerThread thread : this.nonIoThreads) { + try { + thread.join(60 * SECONDS); + } catch (final InterruptedException e) { + throw new IllegalStateException(); + } + } + + System.out.println("Waiting for the I/O worker threads to terminate..."); // NOPMD (Just for example purposes) + for (final WorkerThread thread : this.ioThreads) { + try { + thread.join(60 * SECONDS); + } catch (final InterruptedException e) { + throw new IllegalStateException(); + } + } + + System.out.println("Analysis finished."); // NOPMD (Just for example purposes) + } + + private void createPipeFactories() { + this.pipeFactories = new ConcurrentWorkStealingPipeFactory[4]; + this.pipeFactories[0] = new ConcurrentWorkStealingPipeFactory<File>(); + this.pipeFactories[1] = new ConcurrentWorkStealingPipeFactory<File>(); + this.pipeFactories[2] = new ConcurrentWorkStealingPipeFactory<Pair<File, Integer>>(); + this.pipeFactories[3] = new ConcurrentWorkStealingPipeFactory<Pair<File, Integer>>(); + } + + private IPipeline readerThreadPipeline() { + // create stages + final RepeaterSource<String> repeaterSource = RepeaterSource.create(START_DIRECTORY_NAME, NUM_TOKENS_TO_REPEAT); + repeaterSource.setAccessesDeviceId(1); + final DirectoryName2Files directoryName2Files = new DirectoryName2Files(); + directoryName2Files.setAccessesDeviceId(1); + final Distributor<File> distributor = new Distributor<File>(); + distributor.setAccessesDeviceId(1); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(repeaterSource); + stages.add(directoryName2Files); + stages.add(distributor); + + // connect stages by pipes + QueuePipe.connect(repeaterSource.OUTPUT, directoryName2Files.DIRECTORY_NAME); + QueuePipe.connect(directoryName2Files.fileOutputPort, distributor.genericInputPort); + + repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE)); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return Arrays.asList(repeaterSource); + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + private IPipeline buildNonIoPipeline(final Distributor<File> readerDistributor, final Merger<Pair<File, Integer>> printingMerger) { + // create stages + final Distributor<File> distributor = new Distributor<File>(); + final CountWordsStage countWordsStage0 = new CountWordsStage(); + final CountWordsStage countWordsStage1 = new CountWordsStage(); + final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>(); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(distributor); + stages.add(countWordsStage0); + stages.add(countWordsStage1); + stages.add(merger); + + // connect stages by pipes + SingleProducerSingleConsumerPipe.connect(readerDistributor.getNewOutputPort(), distributor.genericInputPort); + + this.connectWithStealAwarePipe(this.pipeFactories[0], distributor.getNewOutputPort(), countWordsStage0.FILE); + this.connectWithStealAwarePipe(this.pipeFactories[1], distributor.getNewOutputPort(), countWordsStage1.FILE); + this.connectWithStealAwarePipe(this.pipeFactories[2], countWordsStage0.WORDSCOUNT, merger.getNewInputPort()); + this.connectWithStealAwarePipe(this.pipeFactories[3], countWordsStage1.WORDSCOUNT, merger.getNewInputPort()); + + SingleProducerSingleConsumerPipe.connect(merger.outputPort, printingMerger.getNewInputPort()); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return Arrays.asList(distributor); + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + private IPipeline printingThreadPipeline() { + // create stages + final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>(); + merger.setAccessesDeviceId(2); + final OutputWordsCountSink outputWordsCountStage = new OutputWordsCountSink(); + outputWordsCountStage.setAccessesDeviceId(2); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(merger); + stages.add(outputWordsCountStage); + + // connect stages by pipes + QueuePipe.connect(merger.outputPort, outputWordsCountStage.fileWordcountTupleInputPort); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return Arrays.asList(merger); + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + private <A extends ISource, B extends ISink<B>, T> void connectWithStealAwarePipe(final ConcurrentWorkStealingPipeFactory<?> pipeFactory, + final IOutputPort<A, T> sourcePort, final IInputPort<B, T> targetPort) { + @SuppressWarnings("unchecked") + final ConcurrentWorkStealingPipe<T> pipe = (ConcurrentWorkStealingPipe<T>) pipeFactory.create(); + pipe.setSourcePort(sourcePort); + pipe.setTargetPort(targetPort); + } + + /** + * @since 1.10 + */ + public static void main(final String[] args) { + final ConcurrentCountWordsAnalysis analysis = new ConcurrentCountWordsAnalysis(); + analysis.init(); + final long start = System.currentTimeMillis(); + analysis.start(); + final long end = System.currentTimeMillis(); + // analysis.terminate(); + final long duration = end - start; + System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes) + + analysis.analyzeThreads(); + } + + private void analyzeThreads() { + long maxDuration = -1; + WorkerThread maxThread = null; + + // FIXME resolve bug; see analysis results below; + // + // --- Thread[startThread,5,] --- + // {RepeaterSource: numPushedElements={numPushedElements=1000, numTakenElements=1}} + // {DirectoryName2Files: numPushedElements={numPushedElements=16000, numTakenElements=1000}} + // {Distributor: numPushedElements={numPushedElements=16000, numTakenElements=16000}} + // --- Thread[Thread-2,5,] --- + // {Distributor: numPushedElements={numPushedElements=16008, numTakenElements=16008}} + // + // cause: the non-io distributor is executed faster than the io distributor + + for (final WorkerThread thread : this.ioThreads) { + System.out.println("--- " + thread + " ---"); // NOPMD (Just for example purposes) + for (final IStage stage : thread.getPipeline().getStages()) { + System.out.println(stage); // NOPMD (Just for example purposes) + } + + final long durationInNs = thread.getDurationInNs(); + System.out.println(thread + " takes " + TimeUnit.NANOSECONDS.toMillis(durationInNs) + " ms"); + } + + for (final WorkerThread thread : this.nonIoThreads) { + System.out.println("--- " + thread + " ---"); // NOPMD (Just for example purposes) + for (final IStage stage : thread.getPipeline().getStages()) { + System.out.println(stage); // NOPMD (Just for example purposes) + } + + final long durationInNs = thread.getDurationInNs(); + System.out.println(thread + " takes " + TimeUnit.NANOSECONDS.toMillis(durationInNs) + " ms"); + + if (durationInNs > maxDuration) { + maxDuration = durationInNs; + maxThread = thread; + } + } + + System.out.println("maxThread: " + maxThread.toString() + " takes " + TimeUnit.NANOSECONDS.toMillis(maxDuration) + " ms"); // NOPMD (Just for example + // purposes) + } +} diff --git a/src/main/java/teetime/examples/countWords/CountWordsAnalysis.java b/src/main/java/teetime/examples/countWords/CountWordsAnalysis.java new file mode 100644 index 00000000..0da2e7df --- /dev/null +++ b/src/main/java/teetime/examples/countWords/CountWordsAnalysis.java @@ -0,0 +1,150 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countWords; + +import java.io.File; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.framework.sequential.MethodCallPipe; +import teetime.stage.basic.RepeaterSource; +import teetime.stage.basic.distributor.Distributor; +import teetime.stage.basic.merger.Merger; +import teetime.util.Pair; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class CountWordsAnalysis extends Analysis { + + private static final int NUM_TOKENS_TO_REPEAT = 1000; + private static final String START_DIRECTORY_NAME = "."; + + private IPipeline pipeline; + + @Override + public void init() { + super.init(); + + this.pipeline = this.buildNonIoPipeline(); + } + + @Override + public void start() { + super.start(); + try { + this.pipeline.fireStartNotification(); + } catch (final Exception e) { + e.printStackTrace(); + } + + this.pipeline.getStartStages().get(0).execute(); + + try { + this.pipeline.fireStopNotification(); + } catch (final Exception e) { + e.printStackTrace(); + } + } + + private IPipeline buildNonIoPipeline() { + // create stages + final RepeaterSource<String> repeaterSource = RepeaterSource.create(START_DIRECTORY_NAME, NUM_TOKENS_TO_REPEAT); + final DirectoryName2Files findFilesStage = new DirectoryName2Files(); + final Distributor<File> distributor = new Distributor<File>(); + final CountWordsStage countWordsStage0 = new CountWordsStage(); + final CountWordsStage countWordsStage1 = new CountWordsStage(); + final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>(); + final OutputWordsCountSink outputWordsCountStage = new OutputWordsCountSink(); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(repeaterSource); + stages.add(findFilesStage); + stages.add(distributor); + stages.add(countWordsStage0); + stages.add(countWordsStage1); + stages.add(merger); + stages.add(outputWordsCountStage); + + // connect stages by pipes + MethodCallPipe.connect(repeaterSource.OUTPUT, findFilesStage.DIRECTORY_NAME); + MethodCallPipe.connect(findFilesStage.fileOutputPort, distributor.genericInputPort); + MethodCallPipe.connect(distributor.getNewOutputPort(), countWordsStage0.FILE); + MethodCallPipe.connect(distributor.getNewOutputPort(), countWordsStage1.FILE); + MethodCallPipe.connect(countWordsStage0.WORDSCOUNT, merger.getNewInputPort()); + MethodCallPipe.connect(countWordsStage1.WORDSCOUNT, merger.getNewInputPort()); + MethodCallPipe.connect(merger.outputPort, outputWordsCountStage.fileWordcountTupleInputPort); + + repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE)); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends AbstractFilter<?>> getStartStages() { + return Arrays.asList(repeaterSource); + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + public static void main(final String[] args) { + final CountWordsAnalysis analysis = new CountWordsAnalysis(); + analysis.init(); + final long start = System.currentTimeMillis(); + analysis.start(); + final long end = System.currentTimeMillis(); + // analysis.terminate(); + final long duration = end - start; + System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes) + + for (final IStage stage : analysis.pipeline.getStages()) { + if (stage instanceof AbstractFilter<?>) { + System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes) + } + } + + final DirectoryName2Files findFilesStage = (DirectoryName2Files) analysis.pipeline.getStages().get(1); + System.out.println("findFilesStage: " + findFilesStage.getNumFiles()); // NOPMD (Just for example purposes) + + final OutputWordsCountSink outputWordsCountStage = (OutputWordsCountSink) analysis.pipeline.getStages().get(6); + System.out.println("outputWordsCountStage: " + outputWordsCountStage.getNumFiles()); // NOPMD (Just for example purposes) + } +} diff --git a/src/main/java/teetime/examples/countWords/CountWordsStage.java b/src/main/java/teetime/examples/countWords/CountWordsStage.java new file mode 100644 index 00000000..08839930 --- /dev/null +++ b/src/main/java/teetime/examples/countWords/CountWordsStage.java @@ -0,0 +1,80 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countWords; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.regex.Pattern; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.util.Pair; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class CountWordsStage extends AbstractFilter<CountWordsStage> { + + public final IInputPort<CountWordsStage, File> FILE = this.createInputPort(); + + public final IOutputPort<CountWordsStage, Exception> EXCEPTION = this.createOutputPort(); + public final IOutputPort<CountWordsStage, Pair<File, Integer>> WORDSCOUNT = this.createOutputPort(); + + private final Pattern pattern = Pattern.compile("[^\\p{Graph}]"); + + @Override + protected boolean execute(final Context<CountWordsStage> context) { + final File file = context.tryTake(this.FILE); + if (file == null) { + return false; + } + + int wordsCount = 0; + try { + final BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8")); + try { + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.length() > 0) { + final String[] words = this.pattern.split(line); + // System.out.println("" + Arrays.toString(words)); + wordsCount += words.length; + } + } + } finally { + reader.close(); + } + context.put(this.WORDSCOUNT, Pair.of(file, wordsCount)); + } catch (final FileNotFoundException e) { + context.put(this.EXCEPTION, e); + } catch (final IOException e) { + context.put(this.EXCEPTION, e); + } + + return true; + } + +} diff --git a/src/main/java/teetime/examples/countWords/DirectoryName2Files.java b/src/main/java/teetime/examples/countWords/DirectoryName2Files.java new file mode 100644 index 00000000..5c99f80c --- /dev/null +++ b/src/main/java/teetime/examples/countWords/DirectoryName2Files.java @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countWords; + +import java.io.File; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class DirectoryName2Files extends AbstractFilter<DirectoryName2Files> { + + public final IInputPort<DirectoryName2Files, String> DIRECTORY_NAME = this.createInputPort(); + + public final IOutputPort<DirectoryName2Files, File> fileOutputPort = this.createOutputPort(); + + private int numFiles = 0; + + public DirectoryName2Files() { + // this.setAccessesDeviceId(1); + } + + @Override + protected boolean execute(final Context<DirectoryName2Files> context) { + final String inputDir = context.tryTake(this.DIRECTORY_NAME); + if (inputDir == null) { + return false; + } + + final File[] availableFiles = new File(inputDir).listFiles(); + for (final File file : availableFiles) { + if (file.isFile()) { + // this.logger.info("Sending " + file); + context.put(this.fileOutputPort, file); + this.numFiles++; + } + } + + return true; + } + + public int getNumFiles() { + return this.numFiles; + } + +} diff --git a/src/main/java/teetime/examples/countWords/OutputWordsCountSink.java b/src/main/java/teetime/examples/countWords/OutputWordsCountSink.java new file mode 100644 index 00000000..4e9b54a5 --- /dev/null +++ b/src/main/java/teetime/examples/countWords/OutputWordsCountSink.java @@ -0,0 +1,69 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countWords; + +import java.io.File; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.util.Pair; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class OutputWordsCountSink extends AbstractFilter<OutputWordsCountSink> { + + public final IInputPort<OutputWordsCountSink, Pair<File, Integer>> fileWordcountTupleInputPort = this.createInputPort(); + + private int numFiles = 0; + + public OutputWordsCountSink() { + // this.setAccessesDeviceId(2); + } + + @Override + protected boolean execute(final Context<OutputWordsCountSink> context) { + final Pair<File, Integer> pair = context.tryTake(this.fileWordcountTupleInputPort); + if (pair == null) { + return false; + } + + // final File file = pair.getFirst(); + // final Number wordsCount = pair.getSecond(); + // System.out.println(wordsCount + " words in file '" + file.getAbsolutePath() + "'"); // NOPMD (Just for example purposes) + this.numFiles++; + + return true; + } + + /** + * @since 1.10 + * @return + */ + public int getNumFiles() { + return this.numFiles; + } + + @Override + public String toString() { + return super.toString() + ", numFiles = " + this.numFiles; + } + +} diff --git a/src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java b/src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java new file mode 100644 index 00000000..d19e4a7a --- /dev/null +++ b/src/main/java/teetime/examples/countWords/QueuedCountWordsAnalysis.java @@ -0,0 +1,158 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countWords; + +import java.io.File; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.framework.sequential.MethodCallPipe; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.basic.RepeaterSource; +import teetime.stage.basic.distributor.Distributor; +import teetime.stage.basic.merger.Merger; +import teetime.util.Pair; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class QueuedCountWordsAnalysis extends Analysis { + + private static final int NUM_TOKENS_TO_REPEAT = 1000; + private static final String START_DIRECTORY_NAME = "."; + private static final int SECONDS = 1000; + + private WorkerThread workerThread; + + @Override + public void init() { + super.init(); + + final IPipeline pipeline = this.buildNonIoPipeline(); + + this.workerThread = new WorkerThread(pipeline, 0); + } + + @Override + public void start() { + super.start(); + + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + private IPipeline buildNonIoPipeline() { + // create stages + final RepeaterSource<String> repeaterSource = RepeaterSource.create(START_DIRECTORY_NAME, NUM_TOKENS_TO_REPEAT); + final DirectoryName2Files findFilesStage = new DirectoryName2Files(); + final Distributor<File> distributor = new Distributor<File>(); + final CountWordsStage countWordsStage0 = new CountWordsStage(); + final CountWordsStage countWordsStage1 = new CountWordsStage(); + final Merger<Pair<File, Integer>> merger = new Merger<Pair<File, Integer>>(); + final OutputWordsCountSink outputWordsCountStage = new OutputWordsCountSink(); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(repeaterSource); + stages.add(findFilesStage); + stages.add(distributor); + stages.add(countWordsStage0); + stages.add(countWordsStage1); + stages.add(merger); + stages.add(outputWordsCountStage); + + // connect stages by pipes + QueuePipe.connect(repeaterSource.OUTPUT, findFilesStage.DIRECTORY_NAME); + QueuePipe.connect(findFilesStage.fileOutputPort, distributor.genericInputPort); + QueuePipe.connect(distributor.getNewOutputPort(), countWordsStage0.FILE); + QueuePipe.connect(distributor.getNewOutputPort(), countWordsStage1.FILE); + QueuePipe.connect(countWordsStage0.WORDSCOUNT, merger.getNewInputPort()); + QueuePipe.connect(countWordsStage1.WORDSCOUNT, merger.getNewInputPort()); + QueuePipe.connect(merger.outputPort, outputWordsCountStage.fileWordcountTupleInputPort); + + repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE)); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return Arrays.asList(repeaterSource); + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + WorkerThread getWorkerThread() { + return this.workerThread; + } + + public static void main(final String[] args) { + final QueuedCountWordsAnalysis analysis = new QueuedCountWordsAnalysis(); + analysis.init(); + final long start = System.currentTimeMillis(); + analysis.start(); + final long end = System.currentTimeMillis(); + // analysis.terminate(); + final long duration = end - start; + System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes) + + final IPipeline pipeline = analysis.workerThread.getPipeline(); + + for (final IStage stage : pipeline.getStages()) { + if (stage instanceof AbstractFilter<?>) { + System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes) + } + } + + final DirectoryName2Files findFilesStage = (DirectoryName2Files) pipeline.getStages().get(1); + System.out.println("findFilesStage: " + findFilesStage.getNumFiles()); // NOPMD (Just for example purposes) + + final OutputWordsCountSink outputWordsCountStage = (OutputWordsCountSink) pipeline.getStages().get(6); + System.out.println("outputWordsCountStage: " + outputWordsCountStage.getNumFiles()); // NOPMD (Just for example purposes) + } +} diff --git a/src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java b/src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java new file mode 100644 index 00000000..c286a540 --- /dev/null +++ b/src/main/java/teetime/examples/countingObjects/CountingObjectsAnalysis.java @@ -0,0 +1,138 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.examples.countingObjects; + +import java.io.File; +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +import teetime.examples.countWords.DirectoryName2Files; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.framework.sequential.MethodCallPipe; +import teetime.stage.TypeLoggerFilter; +import teetime.stage.basic.RepeaterSource; +import teetime.stage.composite.CycledCountingFilter; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class CountingObjectsAnalysis extends Analysis { + + private IPipeline pipeline; + + @Override + public void init() { + super.init(); + + this.pipeline = this.buildNonIoPipeline(); + } + + private IPipeline buildNonIoPipeline() { + // create stages + final RepeaterSource<String> repeaterSource = RepeaterSource.create(".", 1); + final DirectoryName2Files findFilesStage = new DirectoryName2Files(); + final CycledCountingFilter<File> cycledCountingFilter = CycledCountingFilter.create(new MethodCallPipe<Long>(0L)); + final TypeLoggerFilter<File> typeLoggerFilter = TypeLoggerFilter.create(); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(repeaterSource); + stages.add(findFilesStage); + stages.add(cycledCountingFilter); + stages.add(typeLoggerFilter); + + // connect stages by pipes + MethodCallPipe.connect(repeaterSource.OUTPUT, findFilesStage.DIRECTORY_NAME); + MethodCallPipe.connect(findFilesStage.fileOutputPort, cycledCountingFilter.INPUT_OBJECT); + MethodCallPipe.connect(cycledCountingFilter.RELAYED_OBJECT, typeLoggerFilter.INPUT_OBJECT); + + repeaterSource.START.setAssociatedPipe(new MethodCallPipe<Boolean>(Boolean.TRUE)); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return Arrays.asList(repeaterSource); + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + return pipeline; + } + + @Override + public void start() { + super.start(); + try { + this.pipeline.fireStartNotification(); + } catch (final Exception e) { + e.printStackTrace(); + } + + this.pipeline.getStartStages().get(0).execute(); + + try { + this.pipeline.fireStopNotification(); + } catch (final Exception e) { + e.printStackTrace(); + } + } + + /** + * @since 1.10 + * @param args + */ + public static void main(final String[] args) { + final CountingObjectsAnalysis analysis = new CountingObjectsAnalysis(); + analysis.init(); + final long start = System.currentTimeMillis(); + analysis.start(); + final long end = System.currentTimeMillis(); + // analysis.terminate(); + final long duration = end - start; + System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes) + + for (final IStage stage : analysis.pipeline.getStages()) { + if (stage instanceof AbstractFilter<?>) { + System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes) + } + } + + @SuppressWarnings("unchecked") + final CycledCountingFilter<File> cycledCountingFilter = (CycledCountingFilter<File>) analysis.pipeline.getStages().get(2); + System.out.println("count: " + cycledCountingFilter.getCurrentCount()); // NOPMD (Just for example purposes) + } +} diff --git a/src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java b/src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java new file mode 100644 index 00000000..cfadf9c8 --- /dev/null +++ b/src/main/java/teetime/examples/recordReader/RecordReaderAnalysis.java @@ -0,0 +1,168 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.recordReader; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +import kieker.common.record.IMonitoringRecord; +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Analysis; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IPipe; +import teetime.framework.core.IPipeline; +import teetime.framework.core.ISink; +import teetime.framework.core.ISource; +import teetime.framework.core.IStage; +import teetime.framework.sequential.MethodCallPipe; +import teetime.framework.sequential.QueuePipe; +import teetime.framework.util.BaseStage2StageExtractor; +import teetime.stage.CollectorSink; +import teetime.stage.kieker.File2RecordFilter; +import teetime.stage.kieker.className.ClassNameRegistryRepository; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class RecordReaderAnalysis extends Analysis { + + private static final int SECONDS = 1000; + + private WorkerThread workerThread; + + private File2RecordFilter file2RecordFilter; + private CollectorSink<IMonitoringRecord> collector; + + private ClassNameRegistryRepository classNameRegistryRepository; + + @Override + public void init() { + super.init(); + final IPipeline pipeline = this.buildPipeline(); + this.workerThread = new WorkerThread(pipeline, 0); + } + + @Override + public void start() { + super.start(); + + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } + + void setInputFile(final File file) { + this.file2RecordFilter.fileInputPort.setAssociatedPipe(new MethodCallPipe<File>(file)); + } + + private IPipeline buildPipeline() { + final BaseStage2StageExtractor baseStage2StageExtractor = new BaseStage2StageExtractor(); + + this.classNameRegistryRepository = new ClassNameRegistryRepository(); + // create stages + this.file2RecordFilter = new File2RecordFilter(this.classNameRegistryRepository); + this.collector = new CollectorSink<IMonitoringRecord>(); + + // add each stage to a stage list + final List<IStage> stages = new LinkedList<IStage>(); + stages.addAll(baseStage2StageExtractor.extract(this.file2RecordFilter)); + stages.add(this.collector); + + // connect stages by pipes + final List<IPipe<?>> pipes = new LinkedList<IPipe<?>>(); + pipes.add(this.connectWithSequentialPipe(this.file2RecordFilter.recordOutputPort, this.collector.objectInputPort)); + + final IPipeline pipeline = new IPipeline() { + @Override + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return baseStage2StageExtractor.extract(RecordReaderAnalysis.this.file2RecordFilter); + } + + @Override + public List<IStage> getStages() { + return stages; + } + + @Override + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + @Override + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + private <A extends ISource, B extends ISink<B>, T> IPipe<T> connectWithSequentialPipe(final IOutputPort<A, T> sourcePort, + final IInputPort<B, T> targetPort) { + final IPipe<T> pipe = new QueuePipe<T>(); + pipe.setSourcePort(sourcePort); + pipe.setTargetPort(targetPort); + return pipe; + } + + WorkerThread getWorkerThread() { + return this.workerThread; + } + + public static void main(final String[] args) { + final RecordReaderAnalysis analysis = new RecordReaderAnalysis(); + analysis.init(); + final long start = System.currentTimeMillis(); + analysis.start(); + final long end = System.currentTimeMillis(); + // analysis.terminate(); + final long duration = end - start; + System.out.println("duration: " + duration + " ms"); // NOPMD (Just for example purposes) + + final IPipeline pipeline = analysis.workerThread.getPipeline(); + + for (final IStage stage : pipeline.getStages()) { + if (stage instanceof AbstractFilter<?>) { + System.out.println(stage.getClass().getName() + ": " + ((AbstractFilter<?>) stage).getOverallDurationInNs()); // NOPMD (Just for example purposes) + } + } + } + + void setOutputRecordList(final List<IMonitoringRecord> records) { + this.collector.setObjects(records); + } + + public ClassNameRegistryRepository getClassNameRegistryRepository() { + return this.classNameRegistryRepository; + } + +} diff --git a/src/main/java/teetime/examples/throughput/ThroughputAnalysis.java b/src/main/java/teetime/examples/throughput/ThroughputAnalysis.java new file mode 100644 index 00000000..6a87b53b --- /dev/null +++ b/src/main/java/teetime/examples/throughput/ThroughputAnalysis.java @@ -0,0 +1,122 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.throughput; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.framework.core.Pipeline; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.NoopFilter; +import teetime.stage.basic.ObjectProducer; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ThroughputAnalysis<T> extends Analysis { + + private static final int SECONDS = 1000; + + private WorkerThread workerThread; + + private int numNoopFilters; + + private int numInputObjects; + + private Callable<T> inputObjectCreator; + + @Override + public void init() { + super.init(); + final IPipeline pipeline = this.buildPipeline(this.numNoopFilters); + + this.workerThread = new WorkerThread(pipeline, 0); + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + } + + /** + * @param numNoopFilters + * @since 1.10 + */ + private IPipeline buildPipeline(final int numNoopFilters) { + @SuppressWarnings("unchecked") + final NoopFilter<T>[] noopFilters = new NoopFilter[numNoopFilters]; + // create stages + final ObjectProducer<T> objectProducer = new ObjectProducer<T>(this.numInputObjects, this.inputObjectCreator); + for (int i = 0; i < noopFilters.length; i++) { + noopFilters[i] = new NoopFilter<T>(); + } + + // add each stage to a stage list + final List<IStage> startStages = new LinkedList<IStage>(); + startStages.add(objectProducer); + + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(objectProducer); + stages.addAll(Arrays.asList(noopFilters)); + + // connect stages by pipes + QueuePipe.connect(objectProducer.outputPort, noopFilters[0].inputPort); + for (int i = 1; i < noopFilters.length; i++) { + QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort); + } + + final Pipeline pipeline = new Pipeline(); + pipeline.setStartStages(startStages); + pipeline.setStages(stages); + return pipeline; + } + + @Override + public void start() { + super.start(); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + System.out.println("SchedulingOverhead: " + TimeUnit.NANOSECONDS.toMillis(this.workerThread.computeSchedulingOverheadInNs()) + " ms"); + } + + public int getNumNoopFilters() { + return this.numNoopFilters; + } + + public void setNumNoopFilters(final int numNoopFilters) { + this.numNoopFilters = numNoopFilters; + } + + /** + * @since 1.10 + */ + public void setInput(final int numInputObjects, final Callable<T> inputObjectCreator) { + this.numInputObjects = numInputObjects; + this.inputObjectCreator = inputObjectCreator; + } +} diff --git a/src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java b/src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java new file mode 100644 index 00000000..178d35f6 --- /dev/null +++ b/src/main/java/teetime/examples/throughput/ThroughputTimestampAnalysis.java @@ -0,0 +1,173 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.throughput; + +import java.util.Arrays; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.TimeUnit; + +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.framework.core.Pipeline; +import teetime.framework.sequential.MethodCallPipe; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.CollectorSink; +import teetime.stage.NoopFilter; +import teetime.stage.StartTimestampFilter; +import teetime.stage.StopTimestampFilter; +import teetime.stage.basic.ObjectProducer; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ThroughputTimestampAnalysis extends Analysis { + + private static final int SECONDS = 1000; + + private WorkerThread workerThread; + + private int numNoopFilters; + + private int numInputObjects; + + private Callable<TimestampObject> inputObjectCreator; + + private Collection<TimestampObject> timestampObjects; + + private boolean shouldUseQueue; + + @Override + public void init() { + super.init(); + final IPipeline pipeline = this.buildPipeline(this.numNoopFilters); + + this.workerThread = new WorkerThread(pipeline, 0); + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + } + + /** + * @param numNoopFilters + * @since 1.10 + */ + private IPipeline buildPipeline(final int numNoopFilters) { + @SuppressWarnings("unchecked") + final NoopFilter<TimestampObject>[] noopFilters = new NoopFilter[numNoopFilters]; + // create stages + final ObjectProducer<TimestampObject> objectProducer = new ObjectProducer<TimestampObject>(this.numInputObjects, this.inputObjectCreator); + final StartTimestampFilter startTimestampFilter = new StartTimestampFilter(); + for (int i = 0; i < noopFilters.length; i++) { + noopFilters[i] = new NoopFilter<TimestampObject>(); + } + final StopTimestampFilter stopTimestampFilter = new StopTimestampFilter(); + final CollectorSink<TimestampObject> collectorSink = new CollectorSink<TimestampObject>(this.timestampObjects); + + // add each stage to a stage list + final List<IStage> startStages = new LinkedList<IStage>(); + startStages.add(objectProducer); + + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(objectProducer); + if (this.shouldUseQueue) { + stages.add(startTimestampFilter); + stages.addAll(Arrays.asList(noopFilters)); + stages.add(stopTimestampFilter); + stages.add(collectorSink); + + // connect stages by pipes + QueuePipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort); + QueuePipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort); + for (int i = 1; i < noopFilters.length; i++) { + QueuePipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort); + } + QueuePipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort); + QueuePipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort); + } else { + // connect stages by pipes + MethodCallPipe.connect(objectProducer.outputPort, startTimestampFilter.inputPort); + MethodCallPipe.connect(startTimestampFilter.outputPort, noopFilters[0].inputPort); + for (int i = 1; i < noopFilters.length; i++) { + MethodCallPipe.connect(noopFilters[i - 1].outputPort, noopFilters[i].inputPort); + } + MethodCallPipe.connect(noopFilters[noopFilters.length - 1].outputPort, stopTimestampFilter.inputPort); + MethodCallPipe.connect(stopTimestampFilter.outputPort, collectorSink.objectInputPort); + } + + final Pipeline pipeline = new Pipeline(); + pipeline.setStartStages(startStages); + pipeline.setStages(stages); + return pipeline; + } + + @Override + public void start() { + super.start(); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + + final long schedulingOverheadInNs = this.workerThread.computeSchedulingOverheadInNs(); + final int size = this.workerThread.getSchedulingOverheadsInNs().size(); + System.out.println("scheduling overhead times: " + size); + System.out.println("SchedulingOverhead: " + TimeUnit.NANOSECONDS.toMillis(schedulingOverheadInNs) + " ms"); + System.out.println("avg overhead of iteration: " + + TimeUnit.NANOSECONDS.toMillis(schedulingOverheadInNs / (size / 2)) + " ms"); + System.out.println("ExecutedUnsuccessfullyCount: " + this.workerThread.getExecutedUnsuccessfullyCount()); + } + + public int getNumNoopFilters() { + return this.numNoopFilters; + } + + public void setNumNoopFilters(final int numNoopFilters) { + this.numNoopFilters = numNoopFilters; + } + + /** + * @since 1.10 + */ + public void setInput(final int numInputObjects, final Callable<TimestampObject> inputObjectCreator) { + this.numInputObjects = numInputObjects; + this.inputObjectCreator = inputObjectCreator; + } + + public Collection<TimestampObject> getTimestampObjects() { + return this.timestampObjects; + } + + public void setTimestampObjects(final Collection<TimestampObject> timestampObjects) { + this.timestampObjects = timestampObjects; + } + + public boolean isShouldUseQueue() { + return this.shouldUseQueue; + } + + public void setShouldUseQueue(final boolean shouldUseQueue) { + this.shouldUseQueue = shouldUseQueue; + } +} diff --git a/src/main/java/teetime/examples/throughput/TimestampObject.java b/src/main/java/teetime/examples/throughput/TimestampObject.java new file mode 100644 index 00000000..9aa93791 --- /dev/null +++ b/src/main/java/teetime/examples/throughput/TimestampObject.java @@ -0,0 +1,52 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.throughput; + +/** + * Object for performance evaluation + * + * @author Christian Wulf + * + * @since 1.10 + */ +public final class TimestampObject { + + @SuppressWarnings("PMD.CommentRequired") + private long startTimestamp; + + @SuppressWarnings("PMD.CommentRequired") + private long stopTimestamp; + + @SuppressWarnings("PMD.CommentRequired") + public long getStartTimestamp() { + return this.startTimestamp; + } + + @SuppressWarnings("PMD.CommentRequired") + public void setStartTimestamp(final long startTimestamp) { + this.startTimestamp = startTimestamp; + } + + @SuppressWarnings("PMD.CommentRequired") + public long getStopTimestamp() { + return this.stopTimestamp; + } + + @SuppressWarnings("PMD.CommentRequired") + public void setStopTimestamp(final long stopTimestamp) { + this.stopTimestamp = stopTimestamp; + } +} diff --git a/src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java b/src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java new file mode 100644 index 00000000..29ce10a0 --- /dev/null +++ b/src/main/java/teetime/examples/traceReconstruction/TraceAnalysis.java @@ -0,0 +1,66 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.traceReconstruction; + +import kieker.common.util.filesystem.FSUtil; +import teetime.framework.core.Analysis; +import teetime.stage.predicate.FileExtensionPredicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TraceAnalysis extends Analysis { + + public static void main(final String[] args) { + // FSReader reader + // StringBufferFilter stringBufferFilter + // TimestampFilter timestampFilter + // TraceIdFilter traceIdFilter + // ExecutionRecordTransformationFilter execRecTransformer + // TraceReconstructionFilter mtReconstrFilter + // EventRecordTraceReconstructionFilter eventTraceReconstructionFilter + // EventRecordTraceCounter eventRecordTraceCounter + // TraceEventRecords2ExecutionAndMessageTraceFilter traceEvents2ExecutionAndMessageTraceFilter + + new FileExtensionPredicate(FSUtil.NORMAL_FILE_EXTENSION); + + // reader -> stringBufferFilter + + // stringBufferFilter -> timestampFilter.INPUT_PORT_NAME_EXECUTION + // stringBufferFilter -> timestampFilter.INPUT_PORT_NAME_FLOW + // timestampFilter -> traceIdFilter + + // traceIdFilter -> execRecTransformer + // execRecTransformer -> SYSTEM_ENTITY_FACTORY + + // mtReconstrFilter -> SYSTEM_ENTITY_FACTORY + // execRecTransformer -> mtReconstrFilter + + // traceIdFilter -> eventTraceReconstructionFilter + // eventTraceReconstructionFilter -> eventRecordTraceCounter.INPUT_PORT_NAME_VALID + // eventTraceReconstructionFilter -> eventRecordTraceCounter.INPUT_PORT_NAME_INVALID + + // eventTraceReconstructionFilter -> traceEvents2ExecutionAndMessageTraceFilter + // traceEvents2ExecutionAndMessageTraceFilter -> SYSTEM_ENTITY_FACTORY + } + + @Override + public void init() { + // TODO + } +} diff --git a/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java new file mode 100644 index 00000000..73381658 --- /dev/null +++ b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis.java @@ -0,0 +1,108 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.traceReconstruction; + +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.stage.FileExtensionFilter; +import teetime.stage.io.File2TextLinesFilter; +import teetime.stage.kieker.MonitoringLogDirectory2Files; +import teetime.stage.kieker.className.ClassNameRegistryCreationFilter; +import teetime.stage.kieker.className.ClassNameRegistryRepository; +import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TraceReconstructionAnalysis extends Analysis { + private static final int SECONDS = 1000; + + private WorkerThread workerThread; + + private ClassNameRegistryRepository classNameRegistryRepository; + + @Override + public void init() { + super.init(); + final IPipeline pipeline = this.buildPipeline(); + this.workerThread = new WorkerThread(pipeline, 0); + } + + /** + * @since 1.10 + */ + private IPipeline buildPipeline() { + final ClassNameRegistryCreationFilter classNameRegistryCreationFilter = new ClassNameRegistryCreationFilter(this.classNameRegistryRepository); + final MonitoringLogDirectory2Files directory2FilesFilter = new MonitoringLogDirectory2Files(); + final FileExtensionFilter fileExtensionFilter = new FileExtensionFilter(); + final File2TextLinesFilter file2TextLinesFilter = new File2TextLinesFilter(); + final TextLine2RecordFilter textLine2RecordFilter = new TextLine2RecordFilter(this.classNameRegistryRepository); + + // TODO Auto-generated method stub + + // add each stage to a stage list + final LinkedList<IStage> startStages = new LinkedList<IStage>(); + + final List<IStage> stages = new LinkedList<IStage>(); + + final IPipeline pipeline = new IPipeline() { + @SuppressWarnings("unchecked") + public List<? extends IStage> getStartStages() { + return startStages; + } + + public List<IStage> getStages() { + return stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + }; + + return pipeline; + } + + @Override + public void start() { + super.start(); + + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java new file mode 100644 index 00000000..2db4c764 --- /dev/null +++ b/src/main/java/teetime/examples/traceReconstruction/TraceReconstructionAnalysis2.java @@ -0,0 +1,143 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.traceReconstruction; + +import java.util.LinkedList; +import java.util.List; + +import kieker.analysis.plugin.filter.flow.TraceEventRecords; +import kieker.common.record.IMonitoringRecord; +import kieker.common.record.controlflow.OperationExecutionRecord; +import kieker.common.record.flow.IFlowRecord; +import teetime.framework.concurrent.StageTerminationPolicy; +import teetime.framework.concurrent.WorkerThread; +import teetime.framework.core.Analysis; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.framework.core.Pipeline; +import teetime.framework.sequential.QueuePipe; +import teetime.stage.Cache; +import teetime.stage.CountingFilter; +import teetime.stage.InstanceOfFilter; +import teetime.stage.io.File2TextLinesFilter; +import teetime.stage.kieker.MonitoringLogDirectory2Files; +import teetime.stage.kieker.className.ClassNameRegistryCreationFilter; +import teetime.stage.kieker.className.ClassNameRegistryRepository; +import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter; +import teetime.stage.kieker.traceReconstruction.TraceReconstructionFilter; +import teetime.stage.predicate.IsIMonitoringRecordInRange; +import teetime.stage.predicate.IsOperationExecutionRecordTraceIdPredicate; +import teetime.stage.predicate.PredicateFilter; +import teetime.stage.stringBuffer.StringBufferFilter; +import teetime.stage.util.TextLine; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TraceReconstructionAnalysis2 extends Analysis { + private static final int SECONDS = 1000; + + private WorkerThread workerThread; + + private ClassNameRegistryRepository classNameRegistryRepository; + + @Override + public void init() { + super.init(); + final IPipeline pipeline = this.buildPipeline(); + this.workerThread = new WorkerThread(pipeline, 0); + } + + /** + * @since 1.10 + */ + private IPipeline buildPipeline() { + // predicates TODO + final IsIMonitoringRecordInRange isIMonitoringRecordInRange = new IsIMonitoringRecordInRange(0, 1000); + final IsOperationExecutionRecordTraceIdPredicate isOperationExecutionRecordTraceIdPredicate = new IsOperationExecutionRecordTraceIdPredicate(false, null); + + final ClassNameRegistryCreationFilter classNameRegistryCreationFilter = new ClassNameRegistryCreationFilter(this.classNameRegistryRepository); + final MonitoringLogDirectory2Files directory2FilesFilter = new MonitoringLogDirectory2Files(); + final File2TextLinesFilter file2TextLinesFilter = new File2TextLinesFilter(); + final Cache<TextLine> cache = new Cache<TextLine>(); + + final TextLine2RecordFilter textLine2RecordFilter = new TextLine2RecordFilter(this.classNameRegistryRepository); + final StringBufferFilter<IMonitoringRecord> stringBufferFilter = new StringBufferFilter<IMonitoringRecord>(); + final PredicateFilter<IMonitoringRecord> timestampFilter = new PredicateFilter<IMonitoringRecord>(isIMonitoringRecordInRange); + final PredicateFilter<OperationExecutionRecord> traceIdFilter = new PredicateFilter<OperationExecutionRecord>(isOperationExecutionRecordTraceIdPredicate); + final InstanceOfFilter<IMonitoringRecord, IFlowRecord> instanceOfFilter = new InstanceOfFilter<IMonitoringRecord, IFlowRecord>(IFlowRecord.class); + final TraceReconstructionFilter traceReconstructionFilter = new TraceReconstructionFilter(); + final CountingFilter<TraceEventRecords> countingFilter = new CountingFilter<TraceEventRecords>(); + + // add each stage to a stage list + final LinkedList<IStage> startStages = new LinkedList<IStage>(); + startStages.add(classNameRegistryCreationFilter); + + final List<IStage> stages = new LinkedList<IStage>(); + stages.add(classNameRegistryCreationFilter); + stages.add(directory2FilesFilter); + stages.add(file2TextLinesFilter); + stages.add(cache); + + stages.add(textLine2RecordFilter); + stages.add(stringBufferFilter); + stages.add(timestampFilter); + stages.add(traceIdFilter); + stages.add(instanceOfFilter); + stages.add(traceReconstructionFilter); + stages.add(countingFilter); + + // connect pipes + QueuePipe.connect(classNameRegistryCreationFilter.filePrefixOutputPort, directory2FilesFilter.filePrefixInputPort); + QueuePipe.connect(classNameRegistryCreationFilter.relayDirectoryOutputPort, directory2FilesFilter.directoryInputPort); + QueuePipe.connect(directory2FilesFilter.fileOutputPort, file2TextLinesFilter.fileInputPort); + QueuePipe.connect(file2TextLinesFilter.textLineOutputPort, cache.objectInputPort); + // QueuePipe.connect(XXX, cache.sendInputPort); + QueuePipe.connect(cache.objectOutputPort, textLine2RecordFilter.textLineInputPort); + QueuePipe.connect(textLine2RecordFilter.recordOutputPort, stringBufferFilter.objectInputPort); + QueuePipe.connect(stringBufferFilter.objectOutputPort, timestampFilter.inputPort); + QueuePipe.connect(timestampFilter.matchingOutputPort, traceIdFilter.inputPort); + // QueuePipe.connect(timestampFilter.mismatchingOutputPort, YYY); // ignore this case + QueuePipe.connect(traceIdFilter.matchingOutputPort, instanceOfFilter.inputPort); + // QueuePipe.connect(traceIdFilter.mismatchingOutputPort, traceIdFilter.inputPort); // ignore this case + QueuePipe.connect(XXX, traceReconstructionFilter.timestampInputPort); + QueuePipe.connect(instanceOfFilter.matchingOutputPort, traceReconstructionFilter.recordInputPort); + // QueuePipe.connect(instanceOfFilter.mismatchingOutputPort, instanceOfFilter.inputPort); // ignore this case + QueuePipe.connect(traceReconstructionFilter.traceValidOutputPort, countingFilter.INPUT_OBJECT); + // QueuePipe.connect(traceReconstructionFilter.traceInvalidOutputPort, XXX); // ignore this case + + final Pipeline pipeline = new Pipeline(); + pipeline.setStartStages(startStages); + pipeline.setStages(stages); + return pipeline; + } + + @Override + public void start() { + super.start(); + + this.workerThread.terminate(StageTerminationPolicy.TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION); + + this.workerThread.start(); + try { + this.workerThread.join(60 * SECONDS); + } catch (final InterruptedException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo b/src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo new file mode 100644 index 00000000..07cbd64e --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/ConcurrentPipeline.java.todo @@ -0,0 +1,157 @@ +package kieker.panalysis.framework.concurrent; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import kieker.panalysis.framework.core.IInputPort; +import kieker.panalysis.framework.core.IOutputPort; +import kieker.panalysis.framework.core.IPipe; +import kieker.panalysis.framework.core.ISink; +import kieker.panalysis.framework.core.ISource; +import kieker.panalysis.framework.core.IStage; + +public class ConcurrentPipeline { + + private final Map<IOutputPort<?, ?>, IInputPort<?, ?>> connections = new HashMap<IOutputPort<?, ?>, IInputPort<?, ?>>(); + private final Set<IStage> ioStages = new HashSet<IStage>(); + private final Map<IStage, List<IStage>> standardStages = new HashMap<IStage, List<IStage>>(); + + private final int numCores = Runtime.getRuntime().availableProcessors(); + private int defaultBundleSize = 64; + + public <T> void connect(final IOutputPort<?, T> sourcePort, final IInputPort<?, T> targetPort, final int bundleSize) { + this.connections.put(sourcePort, targetPort); + this.addStage(sourcePort.getOwningStage()); + this.addStage(targetPort.getOwningStage()); + } + + /** + * Connects to ports using the default bundle size + * + * @param sourcePort + * @param targetPort + */ + public <T> void connect(final IOutputPort<?, T> sourcePort, final IInputPort<?, T> targetPort) { + this.connect(sourcePort, targetPort, this.defaultBundleSize); + } + + private void addStage(final IStage owningStage) { + if (owningStage instanceof IoStage) { + this.ioStages.add(owningStage); + } else { + this.standardStages.put(owningStage, null); + } + } + + public void start() { + this.cloneNonIoStages(); + this.connectConcurrentStages(); + this.instantiatePipes(); + } + + private void cloneNonIoStages() { + for (final Entry<IStage, List<IStage>> entry : this.standardStages.entrySet()) { + final List<IStage> concurrentStages = this.createConcurrentStages(entry.getKey()); + entry.setValue(concurrentStages); + } + } + + private List<IStage> createConcurrentStages(final IStage stage) { + final List<IStage> concurrentStages = new LinkedList<IStage>(); + for (int i = 0; i < this.numCores; i++) { + final Class<? extends IStage> stageClazz = stage.getClass(); + // final Constructor<? extends IStage> constructor = stageClazz.getConstructor(Configuration.class); + // final IStage copiedStage = constructor.newInstance(stage.getConfiguration()); // copy by reference since the configuration is read-only + + IStage copiedStage; + try { + copiedStage = stageClazz.newInstance(); + } catch (final InstantiationException e) { + throw new IllegalStateException("The stage to be copied requires a constructor without any parameters.", e); + } catch (final IllegalAccessException e) { + throw new IllegalStateException("The stage to be copied requires a constructor without any parameters.", e); + } + copiedStage.copyAttributes(stage); + concurrentStages.add(copiedStage); + } + return concurrentStages; + } + + private void connectConcurrentStages() { + for (final Entry<IOutputPort<?, ?>, IInputPort<?, ?>> entry : this.connections.entrySet()) { + final IOutputPort<?, ?> sourcePort = entry.getKey(); + final IInputPort<?, ?> targetPort = entry.getValue(); + + final boolean isSourceIoStage = sourcePort.getOwningStage() instanceof IoStage; + final boolean isTargetIoStage = targetPort.getOwningStage() instanceof IoStage; + + if (isSourceIoStage && !isTargetIoStage) { + final List<IStage> concurrentStages = this.standardStages.get(targetPort.getOwningStage()); + final IStage owningSourceStage = sourcePort.getOwningStage(); + for (final IStage concurrentTargetStage : concurrentStages) { + this.connections.put(owningSourceStage.getNewOutputPort(), concurrentTargetStage.getInputPortByIndex(targetPort.getIndex())); + } + } else if (!isSourceIoStage && isTargetIoStage) { + final List<IStage> concurrentStages = this.standardStages.get(sourcePort.getOwningStage()); + final IStage owningTargetStage = targetPort.getOwningStage(); + for (final IStage s : concurrentStages) { + this.connections.put(s.getOutputPortByIndex(sourcePort.getIndex()), owningTargetStage.getNewInputPort()); + } + } else { + final List<IStage> concurrentSourceStages = this.standardStages.get(sourcePort.getOwningStage()); + final List<IStage> concurrentTargetStages = this.standardStages.get(targetPort.getOwningStage()); + for (int i = 0; i < concurrentSourceStages.size(); i++) { + final IStage sourceStage = concurrentSourceStages.get(i); + final IOutputPort<?, ?> otherSourcePort = sourceStage.getOutputPortByIndex(sourcePort.getIndex()); + + final IStage targetStage = concurrentTargetStages.get(i); + final IInputPort<?, ?> otherTargetPort = targetStage.getInputPortByIndex(targetPort.getIndex()); + + this.connections.put(otherSourcePort, otherTargetPort); + } + } + } + } + + @SuppressWarnings("unchecked") + private <T, S0 extends ISource, S1 extends ISink<S1>> void instantiatePipes() { + for (final Entry<IOutputPort<?, ?>, IInputPort<?, ?>> entry : this.connections.entrySet()) { + final IOutputPort<S0, T> sourcePort = (IOutputPort<S0, T>) entry.getKey(); + final IInputPort<S1, T> targetPort = (IInputPort<S1, T>) entry.getValue(); + this.instantiatePipe(sourcePort, targetPort); + } + } + + private <T, P extends IPipe<T>, S0 extends ISource, S1 extends ISink<S1>> + void instantiatePipe(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) { + final boolean isSourceIoStage = sourcePort.getOwningStage() instanceof IoStage; + final boolean isTargetIoStage = targetPort.getOwningStage() instanceof IoStage; + + IPipe<T> pipe; + if (isSourceIoStage && !isTargetIoStage) { + pipe = new ConcurrentWorkStealingPipe<T>(); + } else if (!isSourceIoStage && isTargetIoStage) { + pipe = new SingleProducerSingleConsumerPipe<T>(); + } else if (isSourceIoStage && isTargetIoStage) { + throw new IllegalStateException("It is not allowed to connect two I/O stages."); + } else { + pipe = new ConcurrentWorkStealingPipe<T>(); + } + + pipe.setSourcePort(sourcePort); + pipe.setTargetPort(targetPort); + } + + public void setDefaultBundleSize(final int defaultBundleSize) { + this.defaultBundleSize = defaultBundleSize; + } + + public int getDefaultBundleSize() { + return this.defaultBundleSize; + } +} diff --git a/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java new file mode 100644 index 00000000..fcffbda7 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipe.java @@ -0,0 +1,145 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.concurrent; + +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.concurrent.steal.IStealStrategy; +import teetime.framework.core.AbstractPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.ISink; +import teetime.framework.core.ISource; +import teetime.util.concurrent.workstealing.CircularWorkStealingDeque; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ConcurrentWorkStealingPipe<T> extends AbstractPipe<T> { + + private final CircularWorkStealingDeque<T> circularWorkStealingDeque = new CircularWorkStealingDeque<T>(); + private final IStealStrategy<T> stealStrategy; + + // BETTER use a prioritized list that considers + // <ul> + // <li>the size of each deque to minimize steals + // <li>the core's locality to improve cache access performance + // </ul> + + private List<ConcurrentWorkStealingPipe<T>> pipesToStealFrom; + private int numTakenElements = 0; + + ConcurrentWorkStealingPipe(final IStealStrategy<T> stealStrategy) { + this.stealStrategy = stealStrategy; + } + + public <S0 extends ISource, S1 extends ISink<S1>> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) { + this.setSourcePort(sourcePort); + this.setTargetPort(targetPort); + } + + @Override + protected void putInternal(final T token) { + this.circularWorkStealingDeque.pushBottom(token); + } + + public void putMultiple(final List<T> elements) { + this.circularWorkStealingDeque.pushBottomMultiple(elements); + } + + @Override + protected T tryTakeInternal() { + T record = this.circularWorkStealingDeque.popBottom(); + if (record == null) { + record = this.stealStrategy.steal(this.getTargetPort(), this.pipesToStealFrom); + if (record == null) { + return null; + } + System.out.println("stolen (" + Thread.currentThread() + "): " + record); + } + this.numTakenElements++; + return record; + } + + public T take() { + T record = this.circularWorkStealingDeque.popBottomEx(); + if (record == null) { + record = this.stealStrategy.steal(this.getTargetPort(), this.pipesToStealFrom); + if (record == null) { + return null; + } + } + this.numTakenElements++; + return record; + } + + public List<T> tryTakeMultiple(final int maxItemsToTake) { + // TODO Auto-generated method stub + // BETTER find a way to take multiple elements directly without a loop + return null; + } + + public T read() { + final T record = this.circularWorkStealingDeque.readBottom(); + return record; + } + + public boolean isEmpty() { + return this.circularWorkStealingDeque.isEmpty(); + } + + public T steal() { + return this.circularWorkStealingDeque.steal(); + } + + public List<T> stealMultiple(final int maxItemsToSteal) { + int maxItemsToStealCounter = maxItemsToSteal; + final List<T> stolenElements = new LinkedList<T>(); + while (maxItemsToStealCounter-- > 0) { + final T stolenElement = this.steal(); + if (stolenElement == null) { + break; + } + stolenElements.add(stolenElement); + } + // BETTER find a way to steal multiple elements directly without a loop + return stolenElements; + } + + public List<ConcurrentWorkStealingPipe<T>> getPipesToStealFrom() { + return this.pipesToStealFrom; + } + + public void setPipesToStealFrom(final List<ConcurrentWorkStealingPipe<T>> pipesToStealFrom) { + this.pipesToStealFrom = pipesToStealFrom; + } + + @Override + public void onPipelineStarts() { + this.pipesToStealFrom = new LinkedList<ConcurrentWorkStealingPipe<T>>(this.pipesToStealFrom); + this.pipesToStealFrom.remove(this); + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + "={" + "numTakenElements=" + this.numTakenElements + "}"; + } + +} diff --git a/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java new file mode 100644 index 00000000..c05668c3 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/ConcurrentWorkStealingPipeFactory.java @@ -0,0 +1,46 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent; + +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.concurrent.steal.IStealStrategy; +import teetime.framework.concurrent.steal.StealIfEmptyStrategy; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ConcurrentWorkStealingPipeFactory<T> { + + private final List<ConcurrentWorkStealingPipe<T>> pipes = new LinkedList<ConcurrentWorkStealingPipe<T>>(); + private final IStealStrategy<T> stealStrategy = new StealIfEmptyStrategy<T>(); + + public ConcurrentWorkStealingPipe<T> create() { + final ConcurrentWorkStealingPipe<T> pipe = new ConcurrentWorkStealingPipe<T>(this.stealStrategy); + pipe.setPipesToStealFrom(this.pipes); + + this.pipes.add(pipe); + + return pipe; + } + + public List<ConcurrentWorkStealingPipe<T>> getPipes() { + return this.pipes; + } +} diff --git a/src/main/java/teetime/framework/concurrent/IStageScheduler.java b/src/main/java/teetime/framework/concurrent/IStageScheduler.java new file mode 100644 index 00000000..c798ddb5 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/IStageScheduler.java @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent; + +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IStageScheduler { + + public abstract IStage get(); + + public abstract boolean isAnyStageActive(); + + public abstract void disable(IStage stage); + + public abstract void determineNextStage(IStage stage, boolean executedSuccessfully); + +} diff --git a/src/main/java/teetime/framework/concurrent/IStageWorkList.java b/src/main/java/teetime/framework/concurrent/IStageWorkList.java new file mode 100644 index 00000000..ee3073b9 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/IStageWorkList.java @@ -0,0 +1,40 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent; + +import java.util.Collection; + +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IStageWorkList { + + public abstract void pushAll(Collection<? extends IStage> stages); + + public abstract void pushAll(IOutputPort<?, ?>[] outputPorts); + + public abstract IStage pop(); + + public abstract IStage read(); + + public abstract boolean isEmpty(); + +} diff --git a/src/main/java/teetime/framework/concurrent/NextStageScheduler.java b/src/main/java/teetime/framework/concurrent/NextStageScheduler.java new file mode 100644 index 00000000..8eda5657 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/NextStageScheduler.java @@ -0,0 +1,136 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.concurrent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.util.StopWatch; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class NextStageScheduler implements IStageScheduler { + + protected final Map<IStage, Boolean> statesOfStages = new HashMap<IStage, Boolean>(); + private final Collection<IStage> highestPrioritizedEnabledStages = new ArrayList<IStage>(); + private final IStageWorkList workList; + + private final StopWatch stopWatch = new StopWatch(); + private long durationInNs; + private int iterations; + private final List<Long> durations = new LinkedList<Long>(); + + public NextStageScheduler(final IPipeline pipeline, final int accessesDeviceId) throws Exception { + // this.workList = new StageWorkList(accessesDeviceId, pipeline.getStages().size()); + this.workList = new StageWorkArrayList(pipeline, accessesDeviceId); // faster implementation + + this.highestPrioritizedEnabledStages.addAll(pipeline.getStartStages()); + + this.workList.pushAll(this.highestPrioritizedEnabledStages); + // System.out.println("Initial work list: " + this.workList); + // this.workList.addAll(pipeline.getStages()); + + for (final IStage stage : pipeline.getStages()) { + this.enable(stage); + } + } + + @Override + public IStage get() { + return this.workList.read(); + } + + @Override + public boolean isAnyStageActive() { + // System.out.println("workList: " + this.workList); + return !this.workList.isEmpty(); + } + + protected void enable(final IStage stage) { + // // / TODO consider to move state (enabled/disabled) of stage to stage for performance reasons + this.statesOfStages.put(stage, Boolean.TRUE); + } + + @Override + public void disable(final IStage stage) { + this.statesOfStages.put(stage, Boolean.FALSE); + + if (this.highestPrioritizedEnabledStages.contains(stage)) { + this.highestPrioritizedEnabledStages.remove(stage); + for (final IStage outputStage : stage.getAllOutputStages()) { + if (this.statesOfStages.get(outputStage) == Boolean.TRUE) { + this.highestPrioritizedEnabledStages.add(outputStage); + } + } + } + + stage.fireSignalClosingToAllOutputPorts(); + } + + @Override + public void determineNextStage(final IStage stage, final boolean executedSuccessfully) { + this.iterations++; + + this.stopWatch.start(); + + // final Collection<? extends IStage> outputStages = stage.getContext().getOutputStages(); + final IOutputPort<?, ?>[] outputPorts = stage.getContext().getOutputPorts(); + if (outputPorts.length > 0) { + final boolean inputPortsAreEmpty = stage.getContext().inputPortsAreEmpty(); + if (inputPortsAreEmpty) { + this.workList.pop(); + } + + // TODO consider to not add the stage again if it has a cyclic pipe + // TODO or prioritize non-self stages + // while (outputStages.remove(stage)) { + // } + + this.workList.pushAll(outputPorts); + + stage.getContext().clearSucessors(); + } else { + this.workList.pop(); + } + + if (this.workList.isEmpty()) { + this.workList.pushAll(this.highestPrioritizedEnabledStages); + } + + this.stopWatch.end(); + + this.durationInNs += this.stopWatch.getDuration(); + if ((this.iterations % 10000) == 0) { + this.durations.add(this.durationInNs); + this.durationInNs = 0; + } + } + + public List<Long> getDurations() { + return this.durations; + } +} diff --git a/src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java b/src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java new file mode 100644 index 00000000..40e065db --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/SingleProducerSingleConsumerPipe.java @@ -0,0 +1,84 @@ +package teetime.framework.concurrent; + +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; +import java.util.concurrent.LinkedBlockingDeque; + +import teetime.framework.core.AbstractPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.ISink; +import teetime.framework.core.ISource; +import teetime.util.concurrent.workstealing.CircularWorkStealingDeque; + +public class SingleProducerSingleConsumerPipe<T> extends AbstractPipe<T> { + + // BETTER use a cache-aware queue (see the corresponding paper) + final BlockingQueue<T> queue = new LinkedBlockingDeque<T>(); + + private final Callable<T> blockingTake = new Callable<T>() { + public T call() throws Exception { + return SingleProducerSingleConsumerPipe.this.queue.take(); + } + }; + private final Callable<T> nonBlockingTake = new Callable<T>() { + public T call() throws Exception { + return SingleProducerSingleConsumerPipe.this.queue.poll(); + } + }; + private volatile Callable<T> take = this.blockingTake; + + public static <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) { + final SingleProducerSingleConsumerPipe<T> pipe = new SingleProducerSingleConsumerPipe<T>(); + pipe.setSourcePort(sourcePort); + pipe.setTargetPort(targetPort); + } + + public void putMultiple(final List<T> elements) { + this.queue.addAll(elements); + } + + public T read() { + return this.queue.peek(); + } + + public List<?> tryTakeMultiple(final int numElementsToTake) { + // TODO Auto-generated method stub + return null; + } + + @Override + protected void putInternal(final T token) { + this.queue.add(token); + } + + @Override + protected T tryTakeInternal() { + try { + return this.take.call(); + } catch (final Exception e) { + return null; + } + } + + public T take() { + final T token = this.tryTake(); + if (token == null) { + throw CircularWorkStealingDeque.DEQUE_IS_EMPTY_EXCEPTION; + } + return token; + } + + public boolean isEmpty() { + return this.queue.isEmpty(); + } + + @Override + public void close() { + this.take = this.nonBlockingTake; + super.close(); + this.getTargetPort().getOwningStage().getOwningThread().interrupt(); + } + +} diff --git a/src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java b/src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java new file mode 100644 index 00000000..8eaec85e --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/StageTerminationPolicy.java @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.concurrent; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public enum StageTerminationPolicy { + + TERMINATE_STAGE_NOW, + TERMINATE_STAGE_AFTER_NEXT_EXECUTION, + TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION, + +} diff --git a/src/main/java/teetime/framework/concurrent/StageWorkArrayList.java b/src/main/java/teetime/framework/concurrent/StageWorkArrayList.java new file mode 100644 index 00000000..9f70d0b7 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/StageWorkArrayList.java @@ -0,0 +1,179 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IPipe; +import teetime.framework.core.IPipeCommand; +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StageWorkArrayList implements IStageWorkList { + + private static class SchedulableStage { + public SchedulableStage() {} + + public IStage stage; + public int numToBeExecuted; + } + + private final IPipeline pipeline; + private final int accessesDeviceId; + + /** sorted array where the last stage has highest priority */ + private final SchedulableStage[] stages; + private int firstIndex = Integer.MAX_VALUE; + private int lastIndex = -1; + + /** + * @since 1.10 + */ + public StageWorkArrayList(final IPipeline pipeline, final int accessesDeviceId) { + this.pipeline = pipeline; + this.accessesDeviceId = accessesDeviceId; + final List<IStage> localStages = this.init(); + + this.stages = new SchedulableStage[localStages.size()]; + for (int i = 0; i < localStages.size(); i++) { + final SchedulableStage schedulableStage = new SchedulableStage(); + schedulableStage.stage = localStages.get(i); + schedulableStage.numToBeExecuted = 0; + this.stages[i] = schedulableStage; + } + } + + private List<IStage> init() { + this.setDepthForEachStage(); + + final List<IStage> stageList = new ArrayList<IStage>(this.pipeline.getStages()); + final Comparator<? super IStage> depthComparator = new Comparator<IStage>() { + public int compare(final IStage o1, final IStage o2) { + if (o1.getDepth() == o2.getDepth()) { + return 0; + } else if (o1.getDepth() < o2.getDepth()) { + return -1; + } else { + return 1; + } + } + }; + + Collections.sort(stageList, depthComparator); + + for (int i = 0; i < stageList.size(); i++) { + stageList.get(i).setSchedulingIndex(i); + } + + return stageList; + } + + private void setDepthForEachStage() { + final IPipeCommand setDepthCommand = new IPipeCommand() { + public void execute(final IPipe<?> pipe) throws Exception { + final IStage sourceStage = pipe.getSourcePort().getOwningStage(); + final IStage owningStage = pipe.getTargetPort().getOwningStage(); + if (owningStage.getDepth() == IStage.DEPTH_NOT_SET) { + owningStage.setDepth(sourceStage.getDepth() + 1); + owningStage.notifyOutputPipes(this); + } + } + }; + + for (final IStage startStage : this.pipeline.getStartStages()) { + startStage.setDepth(0); + } + + for (final IStage startStage : this.pipeline.getStartStages()) { + try { + startStage.notifyOutputPipes(setDepthCommand); + } catch (final Exception e) { + throw new IllegalStateException("may not happen", e); + } + } + } + + public void pushAll(final Collection<? extends IStage> stages) { + for (final IStage stage : stages) { + this.push(stage); + } + } + + public void pushAll(final IOutputPort<?, ?>[] outputPorts) { + for (final IOutputPort<?, ?> outputPort : outputPorts) { + if (outputPort != null) { + final IStage targetStage = outputPort.getAssociatedPipe().getTargetPort().getOwningStage(); + this.push(targetStage); + } + } + } + + private void push(final IStage stage) { + if (this.isValid(stage)) { + this.firstIndex = Math.min(stage.getSchedulingIndex(), this.firstIndex); + this.lastIndex = Math.max(stage.getSchedulingIndex(), this.lastIndex); + this.stages[stage.getSchedulingIndex()].numToBeExecuted++; + } + } + + private boolean isValid(final IStage stage) { + final boolean isValid = (stage.getAccessesDeviceId() == this.accessesDeviceId); + if (!isValid) { + // LOG.warn("Invalid stage: stage.accessesDeviceId = " + stage.getAccessesDeviceId() + ", accessesDeviceId = " + this.accessesDeviceId + ", stage = " + + // stage); + } + return isValid; + } + + public IStage pop() { + final SchedulableStage schedulableStage = this.stages[this.lastIndex]; + // schedulableStage.numToBeExecuted--; + schedulableStage.numToBeExecuted = 0; + cond: + if (schedulableStage.numToBeExecuted == 0) + { + for (int i = this.lastIndex - 1; i >= this.firstIndex; i--) { + if (this.stages[i].numToBeExecuted > 0) { + this.lastIndex = i; + break cond; + } + } + this.firstIndex = Integer.MAX_VALUE; + this.lastIndex = -1; + } + return schedulableStage.stage; + } + + public IStage read() { + final SchedulableStage schedulableStage = this.stages[this.lastIndex]; + return schedulableStage.stage; + } + + public boolean isEmpty() { + return this.lastIndex == -1; + } + +} diff --git a/src/main/java/teetime/framework/concurrent/StageWorkList.java b/src/main/java/teetime/framework/concurrent/StageWorkList.java new file mode 100644 index 00000000..6ea46cbc --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/StageWorkList.java @@ -0,0 +1,81 @@ +package teetime.framework.concurrent; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IStage; + +public class StageWorkList implements IStageWorkList { + + // private static final Log LOG = LogFactory.getLog(StageWorkList.class); + + private final List<IStage> workList; + private final int accessesDeviceId; + + public StageWorkList(final int accessesDeviceId, final int initialCapacity) { + this.accessesDeviceId = accessesDeviceId; + this.workList = new ArrayList<IStage>(initialCapacity); + } + + @Override + public void pushAll(final Collection<? extends IStage> stages) { + this.addAll(0, stages); + } + + @Override + public IStage pop() { + return this.workList.remove(0); + } + + @Override + public IStage read() { + return this.workList.get(0); + } + + @Override + public boolean isEmpty() { + return this.workList.isEmpty(); + } + + private boolean isValid(final IStage stage) { + final boolean isValid = (stage.getAccessesDeviceId() == this.accessesDeviceId); + if (!isValid) { + // LOG.warn("Invalid stage: stage.accessesDeviceId = " + stage.getAccessesDeviceId() + ", accessesDeviceId = " + this.accessesDeviceId + ", stage = " + + // stage); + } + return isValid; + } + + private boolean addAll(final int index, final Collection<? extends IStage> collection) { + final Collection<IStage> filteredCollection = new ArrayList<IStage>(collection.size()); + for (final IStage stage : collection) { + this.push(filteredCollection, stage); + } + return this.workList.addAll(index, filteredCollection); + } + + private void push(final Collection<IStage> filteredCollection, final IStage stage) { + if (this.isValid(stage)) { + filteredCollection.add(stage); + } + } + + @Override + public String toString() { + return this.workList.toString(); + } + + public void pushAll(final IOutputPort<?, ?>[] outputPorts) { + final Collection<IStage> filteredCollection = new ArrayList<IStage>(outputPorts.length); + for (final IOutputPort<?, ?> outputPort : outputPorts) { + if (outputPort != null) { + final IStage targetStage = outputPort.getAssociatedPipe().getTargetPort().getOwningStage(); + this.push(filteredCollection, targetStage); + } + } + this.workList.addAll(0, filteredCollection); + } + +} diff --git a/src/main/java/teetime/framework/concurrent/TokenBundle.java b/src/main/java/teetime/framework/concurrent/TokenBundle.java new file mode 100644 index 00000000..dd035cc0 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/TokenBundle.java @@ -0,0 +1,16 @@ +package teetime.framework.concurrent; + +import java.util.List; + +public class TokenBundle<T> { + + private final List<T> tokens; + + public TokenBundle(final List<T> tokens) { + this.tokens = tokens; + } + + public List<T> getTokens() { + return this.tokens; + } +} diff --git a/src/main/java/teetime/framework/concurrent/WorkerThread.java b/src/main/java/teetime/framework/concurrent/WorkerThread.java new file mode 100644 index 00000000..ca2a15c7 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/WorkerThread.java @@ -0,0 +1,207 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.concurrent; + +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.core.IPipeline; +import teetime.framework.core.IStage; +import teetime.util.StopWatch; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class WorkerThread extends Thread { + + private final IPipeline pipeline; + private IStageScheduler stageScheduler; + + private volatile StageTerminationPolicy terminationPolicy; + private volatile boolean shouldTerminate = false; + private final int accessesDeviceId; + private int executedUnsuccessfullyCount; + private final StopWatch stopWatch = new StopWatch(); + private final StopWatch iterationStopWatch = new StopWatch(); + private final List<Long> schedulingOverheadsInNs = new LinkedList<Long>(); + private long durationInNs; + + public WorkerThread(final IPipeline pipeline, final int accessesDeviceId) { + this.pipeline = pipeline; + for (final IStage stage : pipeline.getStages()) { + stage.setOwningThread(this); + } + this.accessesDeviceId = accessesDeviceId; + } + + @Override + public void run() { + try { + this.initDatastructures(); + } catch (final Exception e) { + throw new IllegalStateException(e); + } + + long iterations = 0; + long schedulingOverheadInNs = 0; + this.stopWatch.start(); + + while (this.stageScheduler.isAnyStageActive()) { + iterations++; + this.iterationStopWatch.start(); + + final IStage stage = this.stageScheduler.get(); + + this.startStageExecution(stage); + final boolean executedSuccessfully = stage.execute(); + this.finishStageExecution(stage, executedSuccessfully); + + if (this.shouldTerminate) { + this.executeTerminationPolicy(stage, executedSuccessfully); + } + this.stageScheduler.determineNextStage(stage, executedSuccessfully); + + this.iterationStopWatch.end(); + final long schedulingOverhead = this.iterationStopWatch.getDuration() - stage.getLastDuration(); + schedulingOverheadInNs += schedulingOverhead; + if ((iterations % 10000) == 0) { + this.schedulingOverheadsInNs.add(schedulingOverheadInNs); + schedulingOverheadInNs = 0; + } + } + + this.stopWatch.end(); + this.durationInNs = this.stopWatch.getDuration(); + + final List<Long> durations = ((NextStageScheduler) this.stageScheduler).getDurations(); + long overallDuration = 0; + for (int i = durations.size() / 2; i < durations.size(); i++) { + overallDuration += durations.get(i); + } + // System.out.println("Scheduler determine next stage (" + (durations.size() / 2) + "): " + TimeUnit.NANOSECONDS.toMillis(overallDuration) + " ms"); + + this.cleanUpDatastructures(); + } + + private void executeTerminationPolicy(final IStage executedStage, final boolean executedSuccessfully) { + // System.out.println("WorkerThread.executeTerminationPolicy(): " + this.terminationPolicy + ", executedSuccessfully=" + executedSuccessfully + // + ", mayBeDisabled=" + executedStage.mayBeDisabled()); + + switch (this.terminationPolicy) { + case TERMINATE_STAGE_AFTER_NEXT_EXECUTION: + if (executedStage.mayBeDisabled()) { + this.stageScheduler.disable(executedStage); + } + break; + case TERMINATE_STAGE_AFTER_UNSUCCESSFUL_EXECUTION: + if (!executedSuccessfully) { + if (executedStage.mayBeDisabled()) { + this.stageScheduler.disable(executedStage); + } + } + break; + case TERMINATE_STAGE_NOW: + for (final IStage stage : this.pipeline.getStages()) { + this.stageScheduler.disable(stage); + } + break; + default: + break; + } + } + + private void initDatastructures() throws Exception { + this.pipeline.fireStartNotification(); + this.stageScheduler = new NextStageScheduler(this.pipeline, this.accessesDeviceId); + } + + private void startStageExecution(final IStage stage) { + // System.out.println("Executing stage: " + stage); + } + + private void finishStageExecution(final IStage stage, final boolean executedSuccessfully) { + // System.out.println("Executed stage " + stage + " successfully: " + executedSuccessfully); + if (!executedSuccessfully) { // statistics + this.executedUnsuccessfullyCount++; + } + } + + private void cleanUpDatastructures() { + // System.out.println("Cleaning up datastructures..."); + // System.out.println("Firing stop notification..."); + this.pipeline.fireStopNotification(); + // System.out.println("Thread terminated:" + this); + // System.out.println(this.getName() + ": executedUnsuccessfullyCount=" + this.executedUnsuccessfullyCount); + } + + public IPipeline getPipeline() { + return this.pipeline; + } + + // BETTER remove this method since it is not intuitive; add a check to onStartPipeline so that a stage automatically disables itself if it has no input ports + public void terminate(final StageTerminationPolicy terminationPolicyToUse) { + for (final IStage startStage : this.pipeline.getStartStages()) { + startStage.fireSignalClosingToAllInputPorts(); + } + + this.setTerminationPolicy(terminationPolicyToUse); + } + + /** + * If not set, this thread will run infinitely. + * + * @param terminationPolicyToUse + */ + public void setTerminationPolicy(final StageTerminationPolicy terminationPolicyToUse) { + this.terminationPolicy = terminationPolicyToUse; + this.shouldTerminate = true; + } + + public int getExecutedUnsuccessfullyCount() { + return this.executedUnsuccessfullyCount; + } + + public List<Long> getSchedulingOverheadsInNs() { + return this.schedulingOverheadsInNs; + } + + /** + * @since 1.10 + */ + public long getDurationInNs() { + return this.durationInNs; + } + + /** + * Uses the last half of values to compute the scheduling overall overhead in ns + * + * @since 1.10 + */ + public long computeSchedulingOverheadInNs() { + final int size = this.schedulingOverheadsInNs.size(); + + long schedulingOverheadInNs = 0; + for (int i = size / 2; i < size; i++) { + final Long iterationOverhead = this.schedulingOverheadsInNs.get(i); + schedulingOverheadInNs += iterationOverhead; + } + + return schedulingOverheadInNs; + } +} diff --git a/src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java b/src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java new file mode 100644 index 00000000..cd38a195 --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/steal/IStealStrategy.java @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent.steal; + +import java.util.Collection; + +import teetime.framework.concurrent.ConcurrentWorkStealingPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IStealStrategy<T> { + + <S extends IStage> T steal(IInputPort<S, T> inputPort, Collection<ConcurrentWorkStealingPipe<T>> pipesToStealFrom); + +} diff --git a/src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java b/src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java new file mode 100644 index 00000000..a042054e --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/steal/StealIfEmptyStrategy.java @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent.steal; + +import java.util.Collection; + +import teetime.framework.concurrent.ConcurrentWorkStealingPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StealIfEmptyStrategy<T> implements IStealStrategy<T> { + + public <S extends IStage> T steal(final IInputPort<S, T> inputPort, final Collection<ConcurrentWorkStealingPipe<T>> pipesToStealFrom) { + for (final ConcurrentWorkStealingPipe<T> pipe : pipesToStealFrom) { + final T stolenElement = pipe.steal(); + if (stolenElement != null) { + return stolenElement; + } + } + // BETTER improve stealing efficiency by stealing multiple elements at once + return null; // do not expose internal impl details (here: CircularWorkStealingDeque); instead return null + } + +} diff --git a/src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java b/src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java new file mode 100644 index 00000000..f153f60b --- /dev/null +++ b/src/main/java/teetime/framework/concurrent/steal/StealIfMayBeDisabledStrategy.java @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.concurrent.steal; + +import java.util.Collection; + +import teetime.framework.concurrent.ConcurrentWorkStealingPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StealIfMayBeDisabledStrategy<T> implements IStealStrategy<T> { + + public <S extends IStage> T steal(final IInputPort<S, T> inputPort, final Collection<ConcurrentWorkStealingPipe<T>> pipesToStealFrom) { + if (inputPort.getOwningStage().mayBeDisabled()) { + for (final ConcurrentWorkStealingPipe<T> pipe : pipesToStealFrom) { + final T stolenElement = pipe.steal(); + if (stolenElement != null) { + return stolenElement; + } + } + } + // BETTER improve stealing efficiency by stealing multiple elements at once + return null; // do not expose internal impl details (here: CircularWorkStealingDeque); instead return null + } + +} diff --git a/src/main/java/teetime/framework/core/AbstractFilter.java b/src/main/java/teetime/framework/core/AbstractFilter.java new file mode 100644 index 00000000..4b79473c --- /dev/null +++ b/src/main/java/teetime/framework/core/AbstractFilter.java @@ -0,0 +1,334 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import teetime.util.StopWatch; +import teetime.util.concurrent.workstealing.DequePopException; + +/** + * + * @author Christian Wulf + * + * @since 1.10 + * + * @param <S> + * the extending stage + * + */ +public abstract class AbstractFilter<S extends IStage> extends AbstractStage implements ISink<S>, ISource, IPortListener<S> { + + protected volatile boolean mayBeDisabled; // BETTER write only non-concurrent code in a stage + + /** + * @author Christian Wulf + * + * @since 1.10 + */ + public enum StageState { + UNINITIALIZED, PIPELINE_STARTED, PIPELINE_STOPPED + } + + /** + * Indicates whether this stage has (already) been initialized.<br> + * <i>This attribute prevents this stage to be initialized more than once.</i> + */ + private StageState state = StageState.UNINITIALIZED; + + private int depth = IStage.DEPTH_NOT_SET; + private int schedulingIndex; + + private final List<IInputPort<S, ?>> inputPorts = new ArrayList<IInputPort<S, ?>>(); + private final List<IInputPort<S, ?>> readOnlyInputPorts = Collections.unmodifiableList(this.inputPorts); + + private final List<IOutputPort<S, ?>> outputPorts = new ArrayList<IOutputPort<S, ?>>(); + private final List<IOutputPort<S, ?>> readOnlyOutputPorts = Collections.unmodifiableList(this.outputPorts); + + private Context<S> context; + + private final IPipeCommand closeCommand = new IPipeCommand() { + public void execute(final IPipe<?> pipe) throws Exception { + pipe.close(); + } + }; + + private final IPipeCommand pipelineStartsCommand = new IPipeCommand() { + public void execute(final IPipe<?> pipe) throws Exception { + pipe.notifyPipelineStarts(); + } + }; + + private int enabledInputPorts = 0; + /** + * 0=in-memory, x>0=disk0, disk1, display0, display1, socket0, socket1 etc. + */ + private int accessesDeviceId = 0; + + private final StopWatch stopWatch = new StopWatch(); + private long overallDurationInNs = 0; + + private long lastDuration; + + public int getAccessesDeviceId() { + return this.accessesDeviceId; + } + + public void setAccessesDeviceId(final int accessesDeviceId) { + this.accessesDeviceId = accessesDeviceId; + } + + // BETTER return a limited context that allows "read" only + public Context<S> getContext() { + return this.context; + } + + /** + * @since 1.10 + */ + public final boolean execute() { + boolean success = false; + try { + success = this.executeLogged(this.context); + if (success) { // deprecated boolean return value + this.context.clear(); + } else { + this.context.rollback(); + } + } catch (final DequePopException e) { + this.context.rollback(); + } catch (final Exception e) { + this.logger.error("Error in stage execution", e); + } + return success; + } + + private boolean executeLogged(final Context<S> context) { + this.stopWatch.start(); + try { + final boolean success = this.execute(context); + return success; + } finally { + this.stopWatch.end(); + this.lastDuration = this.stopWatch.getDuration(); + this.overallDurationInNs += this.lastDuration; + } + } + + protected abstract boolean execute(Context<S> context); + + public final void notifyPipelineStarts() throws Exception { + if (this.state == StageState.UNINITIALIZED) { + this.state = StageState.PIPELINE_STARTED; + this.onPipelineStarts(); + this.notifyOutputPipes(this.pipelineStartsCommand); + } + } + + /** + * This method is called exactly once iff the pipeline is started. + * + * @throws Exception + * @since 1.10 + */ + public void onPipelineStarts() throws Exception { + this.context = new Context<S>(this, this.readOnlyInputPorts); + } + + /** + * @since 1.10 + */ + public void notifyOutputPipes(final IPipeCommand pipeCommand) throws Exception { + for (final IOutputPort<S, ?> outputPort : this.readOnlyOutputPorts) { + final IPipe<?> associatedPipe = outputPort.getAssociatedPipe(); + if (associatedPipe != null) { + pipeCommand.execute(associatedPipe); + } // else: ignore unconnected port + } + } + + public final void notifyPipelineStops() { + if (this.state != StageState.PIPELINE_STOPPED) { + this.state = StageState.PIPELINE_STOPPED; + this.onPipelineStops(); + } + } + + /** + * This method is called exactly once iff the pipeline is stopped. + * + * @since 1.10 + */ + public void onPipelineStops() { + // default empty implementation + } + + /** + * @since 1.10 + */ + public void onPortIsClosed(final IInputPort<S, ?> inputPort) { + // inputPort.setState(IInputPort.State.CLOSING); + this.enabledInputPorts--; + // this.logger.info("Closed " + "(" + this.enabledInputPorts + " remaining) " + inputPort + " of " + this); + + if (this.enabledInputPorts < 0) { + this.logger.error("Closed port more than once: portIndex=" + inputPort.getIndex() + " for stage " + this); + } + + this.checkWhetherThisStageMayBeDisabled(); + } + + /** + * @since 1.10 + */ + private void checkWhetherThisStageMayBeDisabled() { + if (this.enabledInputPorts == 0) { + this.mayBeDisabled = true; + // this.logger.info(this.toString() + " can now be disabled by the pipeline scheduler."); + } + } + + /** + * @since 1.10 + */ + public void fireSignalClosingToAllInputPorts() { + // this.logger.info("Fire closing signal to all input ports of: " + this); + + if (!this.inputPorts.isEmpty()) { + for (final IInputPort<S, ?> port : this.inputPorts) { + port.close(); + } + } else { + this.checkWhetherThisStageMayBeDisabled(); + } + } + + /** + * @since 1.10 + */ + public void fireSignalClosingToAllOutputPorts() { + try { + this.notifyOutputPipes(this.closeCommand); + } catch (final Exception e) { + throw new IllegalStateException("may not happen"); + } + } + + /** + * @since 1.10 + */ + public boolean mayBeDisabled() { + return this.mayBeDisabled; + } + + @Override + public String toString() { + final String s = super.toString(); + return "{" + s + ": " + "numPushedElements=" + this.context + "}"; + // return s; + } + + /** + * @since 1.10 + * @return a new input port that accepts elements of the particular type that is specified in the variable declaration. + */ + protected <T> IInputPort<S, T> createInputPort() { + @SuppressWarnings("unchecked") + final InputPortImpl<S, T> inputPort = new InputPortImpl<S, T>((S) this); + inputPort.setIndex(this.inputPorts.size()); + this.inputPorts.add(inputPort); + inputPort.setPortListener(this); + this.enabledInputPorts++; + return inputPort; + } + + /** + * @since 1.10 + * @return a new output port that accepts elements of the particular type that is specified in the variable declaration. + */ + protected <T> IOutputPort<S, T> createOutputPort() { + @SuppressWarnings("unchecked") + final OutputPortImpl<S, T> outputPort = new OutputPortImpl<S, T>((S) this); + outputPort.setIndex(this.outputPorts.size()); + this.outputPorts.add(outputPort); + return outputPort; + } + + /** + * @since 1.10 + */ + @SuppressWarnings("unchecked") + public List<IInputPort<S, ?>> getInputPorts() { + return this.readOnlyInputPorts; + } + + /** + * @since 1.10 + */ + @SuppressWarnings("unchecked") + public List<IOutputPort<S, ?>> getOutputPorts() { + return this.readOnlyOutputPorts; + } + + @Override + public Collection<? extends IStage> getAllOutputStages() { + final Collection<IStage> outputStages = new LinkedList<IStage>(); + for (final IOutputPort<S, ?> outputPort : this.readOnlyOutputPorts) { + final IPipe<?> associatedPipe = outputPort.getAssociatedPipe(); + if (associatedPipe != null) { + outputStages.add(associatedPipe.getTargetPort().getOwningStage()); + } + } + return outputStages; + } + + public IInputPort<?, ?> getInputPortByIndex(final int index) { + return this.readOnlyInputPorts.get(index); + } + + public IOutputPort<?, ?> getOutputPortByIndex(final int index) { + return this.readOnlyOutputPorts.get(index); + } + + public long getOverallDurationInNs() { + return this.overallDurationInNs; + } + + public long getLastDuration() { + return this.lastDuration; + } + + public int getDepth() { + return this.depth; + } + + public void setDepth(final int depth) { + this.depth = depth; + } + + public int getSchedulingIndex() { + return this.schedulingIndex; + } + + public void setSchedulingIndex(final int schedulingIndex) { + this.schedulingIndex = schedulingIndex; + } +} diff --git a/src/main/java/teetime/framework/core/AbstractMultiPort.java b/src/main/java/teetime/framework/core/AbstractMultiPort.java new file mode 100644 index 00000000..b40b0910 --- /dev/null +++ b/src/main/java/teetime/framework/core/AbstractMultiPort.java @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +abstract class AbstractMultiPort<S extends IStage, T> { + + private final Set<IPipe<T>> associatedPipes = new HashSet<IPipe<T>>(); + private final Set<IPipe<T>> readOnlyAssociatedPipes = Collections.unmodifiableSet(this.associatedPipes); + + private S owningStage; + + public void addAssociatedPipe(final IPipe<T> associatedPipe) { + this.associatedPipes.add(associatedPipe); + } + + public Set<IPipe<T>> getAssociatedPipes() { + return this.readOnlyAssociatedPipes; + } + + public S getOwningStage() { + return this.owningStage; + } + + public void setOwningStage(final S owningStage) { + this.owningStage = owningStage; + } +} diff --git a/src/main/java/teetime/framework/core/AbstractPipe.java b/src/main/java/teetime/framework/core/AbstractPipe.java new file mode 100644 index 00000000..1e596bf5 --- /dev/null +++ b/src/main/java/teetime/framework/core/AbstractPipe.java @@ -0,0 +1,115 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + * + * @param <T> + * The type of the pipe + * @param <P> + * the extending pipe + */ +public abstract class AbstractPipe<T> implements IPipe<T> { + + /** + * @author Christian Wulf + * + * @since 1.10 + */ + public enum PipeState { + UNINITIALIZED, PIPELINE_STARTED, PIPELINE_STOPPED + } + + private PipeState state = PipeState.UNINITIALIZED; + + private IOutputPort<?, ? extends T> sourcePort; + private IInputPort<?, T> targetPort; + + public IOutputPort<?, ? extends T> getSourcePort() { + return this.sourcePort; + } + + public IInputPort<?, T> getTargetPort() { + return this.targetPort; + } + + public <S extends ISource, A extends T> void setSourcePort(final IOutputPort<S, A> sourcePort) { + sourcePort.setAssociatedPipe(this); + this.sourcePort = sourcePort; + } + + public <S extends ISink<S>, A extends T> void setTargetPort(final IInputPort<S, T> targetPort) { + targetPort.setAssociatedPipe(this); + this.targetPort = targetPort; + } + + // BETTER remove if it does not add any new functionality + protected abstract void putInternal(T token); + + public void put(final T token) { + this.putInternal(token); + } + + // BETTER remove if it does not add any new functionality + protected abstract T tryTakeInternal(); + + public final T tryTake() { + return this.tryTakeInternal(); + } + + public final void notifyPipelineStarts() throws Exception { + if (this.state == PipeState.UNINITIALIZED) { + this.state = PipeState.PIPELINE_STARTED; + this.onPipelineStarts(); + this.getTargetPort().getOwningStage().notifyPipelineStarts(); + } + } + + /** + * This method is called exactly once iff the pipeline is started. + * + * @since 1.10 + */ + public void onPipelineStarts() { + // empty default implementation + } + + public final void notifyPipelineStops() { + if (this.state != PipeState.PIPELINE_STOPPED) { + this.state = PipeState.PIPELINE_STOPPED; + this.onPipelineStops(); + this.getTargetPort().getOwningStage().notifyPipelineStops(); + } + } + + /** + * This method is called exactly once iff the pipeline is stopped. + * + * @since 1.10 + */ + public void onPipelineStops() { + // empty default implementation + } + + public void close() { + this.targetPort.close(); + } + +} diff --git a/src/main/java/teetime/framework/core/AbstractPort.java b/src/main/java/teetime/framework/core/AbstractPort.java new file mode 100644 index 00000000..4bcf752e --- /dev/null +++ b/src/main/java/teetime/framework/core/AbstractPort.java @@ -0,0 +1,37 @@ +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +abstract class AbstractPort<S extends IStage, T> implements IPort<S, T> { + + private IPipe<? super T> associatedPipe; + private S owningStage; + private int index; + + public <A extends T> void setAssociatedPipe(final IPipe<? super T> associatedPipe) { + this.associatedPipe = associatedPipe; + } + + public IPipe<? super T> getAssociatedPipe() { + return this.associatedPipe; + } + + public S getOwningStage() { + return this.owningStage; + } + + public void setOwningStage(final S owningStage) { + this.owningStage = owningStage; + } + + public int getIndex() { + return this.index; + } + + void setIndex(final int index) { + this.index = index; + } +} diff --git a/src/main/java/teetime/framework/core/AbstractStage.java b/src/main/java/teetime/framework/core/AbstractStage.java new file mode 100644 index 00000000..46f5e789 --- /dev/null +++ b/src/main/java/teetime/framework/core/AbstractStage.java @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +import java.util.UUID; + +import kieker.common.logging.Log; +import kieker.common.logging.LogFactory; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public abstract class AbstractStage implements IStage { + + private final String id; + /** + * A unique logger instance per stage instance + */ + protected Log logger; + private IPipeline owningPipeline; + + private Thread owningThread; + + public AbstractStage() { + this.id = UUID.randomUUID().toString(); // the id should only be represented by a UUID, not additionally by the class name + this.logger = LogFactory.getLog(this.id); + } + + public String getId() { + return this.id; + } + + public IPipeline getOwningPipeline() { + return this.owningPipeline; + } + + public void setOwningPipeline(final IPipeline owningPipeline) { + this.owningPipeline = owningPipeline; + } + + @Override + public Thread getOwningThread() { + return this.owningThread; + } + + public void setOwningThread(final Thread owningThread) { + this.owningThread = owningThread; + } + + @Override + public String toString() { + // return "{" + "class=" + this.getClass().getSimpleName() + ", id=" + this.id + "}"; + return this.getClass().getSimpleName() + "(" + Thread.currentThread() + ")"; + } +} diff --git a/src/main/java/teetime/framework/core/Analysis.java b/src/main/java/teetime/framework/core/Analysis.java new file mode 100644 index 00000000..915773cd --- /dev/null +++ b/src/main/java/teetime/framework/core/Analysis.java @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class Analysis { + + public void init() { + + } + + public void start() { + + } + + public void terminate() { + + } +} diff --git a/src/main/java/teetime/framework/core/CompositeFilter.java b/src/main/java/teetime/framework/core/CompositeFilter.java new file mode 100644 index 00000000..353913b0 --- /dev/null +++ b/src/main/java/teetime/framework/core/CompositeFilter.java @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public abstract class CompositeFilter implements IBaseStage { + + protected final List<IBaseStage> schedulableStages = new ArrayList<IBaseStage>(); + + public List<IBaseStage> getSchedulableStages() { + return this.schedulableStages; + } +} diff --git a/src/main/java/teetime/framework/core/Context.java b/src/main/java/teetime/framework/core/Context.java new file mode 100644 index 00000000..e4990444 --- /dev/null +++ b/src/main/java/teetime/framework/core/Context.java @@ -0,0 +1,169 @@ +package teetime.framework.core; + +import java.util.ArrayList; +import java.util.List; + +public class Context<S extends IStage> { + + // private final Map<IPipe<Object>, List<Object>> pipesTakenFrom; + // private final Set<IStage> pipesPutTo = new HashSet<IStage>(); + + /** + * @author Christian Wulf + * + * @since 1.10 + */ + private static class InputPortContainer { + public final List<Object> takenElements = new ArrayList<Object>(); + public IPipe<Object> pipe; + + public InputPortContainer() {} + } + + private final InputPortContainer[] inputPortContainers; + private final IOutputPort<S, ?>[] outputPorts; + + // statistics values + private int numPushedElements = 0; + private int numTakenElements = 0; + + @SuppressWarnings("unchecked") + public Context(final IStage owningStage, final List<IInputPort<S, ?>> allTargetPorts) { + // this.pipesTakenFrom = this.createPipeMap(allTargetPorts); + this.inputPortContainers = this.createInputPortLists(owningStage.getInputPorts()); + this.outputPorts = new IOutputPort[owningStage.getOutputPorts().size()]; + } + + @SuppressWarnings("unchecked") + private InputPortContainer[] createInputPortLists(final List<IInputPort<IStage, ?>> inputPorts) { + final InputPortContainer[] inputPortContainers = new InputPortContainer[inputPorts.size()]; + for (int i = 0; i < inputPorts.size(); i++) { + inputPortContainers[i] = new InputPortContainer(); + inputPortContainers[i].pipe = (IPipe<Object>) inputPorts.get(i).getAssociatedPipe(); + } + return inputPortContainers; + } + + /** + * @since 1.10 + */ + public <T> void put(final IOutputPort<S, T> port, final T object) { + final IPipe<? super T> associatedPipe = port.getAssociatedPipe(); + if (associatedPipe == null) { + return; // ignore unconnected port + // BETTER return a NullObject rather than checking for null + } + associatedPipe.put(object); + + this.outputPorts[port.getIndex()] = port; + // this.pipesPutTo.add(associatedPipe.getTargetPort().getOwningStage()); + this.numPushedElements++; + } + + /** + * + * @param inputPort + * @return + * @since 1.10 + */ + public <T> T tryTake(final IInputPort<S, T> inputPort) { + final IPipe<? super T> associatedPipe = inputPort.getAssociatedPipe(); + final T token = associatedPipe.tryTake(); + if (token != null) { + this.logTransaction(inputPort, token); + } + return token; + } + + /** + * + * @param inputPort + * @return + * @since 1.10 + */ + public <T> T take(final IInputPort<S, T> inputPort) { + final IPipe<? super T> associatedPipe = inputPort.getAssociatedPipe(); + final T token = associatedPipe.take(); + if (token != null) { + this.logTransaction(inputPort, token); + } + return token; + } + + private <T> void logTransaction(final IInputPort<S, T> inputPort, final T token) { + final InputPortContainer inputPortContainer = this.inputPortContainers[inputPort.getIndex()]; + // final List<Object> tokenList = this.pipesTakenFrom.get(inputPort); + inputPortContainer.takenElements.add(token); + + this.numTakenElements++; + } + + /** + * + * @param inputPort + * @return + * + * @since 1.10 + */ + public <T> T read(final IInputPort<S, T> inputPort) { + final IPipe<? super T> associatedPipe = inputPort.getAssociatedPipe(); + return associatedPipe.read(); + } + + void clear() { + // for (final List<Object> takenElements : this.pipesTakenFrom.values()) { + for (final InputPortContainer inputPortContainer : this.inputPortContainers) { + inputPortContainer.takenElements.clear(); + } + } + + void rollback() { + // for (final Entry<IPipe<Object>, List<Object>> entry : this.pipesTakenFrom.entrySet()) { + // final IPipe<Object> associatedPipe = entry.getKey(); + // final List<Object> takenElements = entry.getValue(); + + for (final InputPortContainer inputPortContainer : this.inputPortContainers) { + + for (int k = inputPortContainer.takenElements.size() - 1; k >= 0; k--) { + final Object element = inputPortContainer.takenElements.get(k); + inputPortContainer.pipe.put(element); + } + + this.numTakenElements -= inputPortContainer.takenElements.size(); + } + } + + @Override + public String toString() { + return "{" + "numTakenElements=" + this.numTakenElements + ", " + "numPushedElements=" + this.numPushedElements + "}"; + } + + /** + * @return <code>true</code> iff all input ports are empty, otherwise <code>false</code>. + */ + public boolean inputPortsAreEmpty() { + for (final InputPortContainer inputPortContainer : this.inputPortContainers) { + if (!inputPortContainer.pipe.isEmpty()) { + return false; + } + } + return true; + } + + /** + * @since 1.10 + */ + public void clearSucessors() { + for (int i = 0; i < this.outputPorts.length; i++) { + this.outputPorts[i] = null; + } + } + + /** + * @return + * @since 1.10 + */ + public IOutputPort<S, ?>[] getOutputPorts() { + return this.outputPorts; + } +} diff --git a/src/main/java/teetime/framework/core/Description.java b/src/main/java/teetime/framework/core/Description.java new file mode 100644 index 00000000..fda9e089 --- /dev/null +++ b/src/main/java/teetime/framework/core/Description.java @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.FIELD }) +public @interface Description { + + String value(); + +} diff --git a/src/main/java/teetime/framework/core/IBaseStage.java b/src/main/java/teetime/framework/core/IBaseStage.java new file mode 100644 index 00000000..99c03745 --- /dev/null +++ b/src/main/java/teetime/framework/core/IBaseStage.java @@ -0,0 +1,25 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IBaseStage { + +} diff --git a/src/main/java/teetime/framework/core/IInputPort.java b/src/main/java/teetime/framework/core/IInputPort.java new file mode 100644 index 00000000..3d834da7 --- /dev/null +++ b/src/main/java/teetime/framework/core/IInputPort.java @@ -0,0 +1,40 @@ +package teetime.framework.core; + +/** + * + * @author Christian Wulf + * + * @param <S> + * the stage, this port belongs to<br> + * <i>(used for ensuring type safety)</i> + * @param <T> + */ +public interface IInputPort<S extends IStage, T> extends IPort<S, T> { + + /** + * @since 1.10 + */ + enum State { + OPEN, CLOSING + } + + /** + * @since 1.10 + */ + public abstract State getState(); + + /** + * @since 1.10 + */ + public abstract void setState(final State state); + + /** + * @since 1.10 + */ + public abstract void setPortListener(final IPortListener<S> stageListener); + + /** + * @since 1.10 + */ + public void close(); +} diff --git a/src/main/java/teetime/framework/core/IOutputPort.java b/src/main/java/teetime/framework/core/IOutputPort.java new file mode 100644 index 00000000..3d8b8c06 --- /dev/null +++ b/src/main/java/teetime/framework/core/IOutputPort.java @@ -0,0 +1,14 @@ +package teetime.framework.core; + +/** + * + * @author Christian Wulf + * + * @param <S> + * the stage, this port belongs to<br> + * <i>(used for ensuring type safety)</i> + * @param <T> + */ +public interface IOutputPort<S extends IStage, T> extends IPort<S, T> { + +} diff --git a/src/main/java/teetime/framework/core/IPipe.java b/src/main/java/teetime/framework/core/IPipe.java new file mode 100644 index 00000000..148e88f6 --- /dev/null +++ b/src/main/java/teetime/framework/core/IPipe.java @@ -0,0 +1,123 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +import java.util.List; + +import teetime.util.concurrent.workstealing.DequePopException; + +/** + * + * @author Christian Wulf + * + * @since 1.10 + * + * @param <T> + * the type of elements this pipe may contain + * @param <P> + */ +public interface IPipe<T> { + + /** + * @since 1.10 + */ + void put(T object); + + /** + * @since 1.10 + */ + void putMultiple(List<T> elements); + + // Let this uncommented for documentation purpose:<br> + // Since the concurrent execution does not base on locks, it is difficult to define a blocking take. Since there is no definition of blocking in the sequential + // execution, we do not define a blocking take in this interface + // /** + // * @since 1.10 + // */ + // @Deprecated + // T take(); + + /** + * @return and removes the next element if the pipe is not empty, otherwise <code>null</code> + * + * @since 1.10 + */ + <A extends T> A tryTake(); + + /** + * + * @return and removes the next element if the pipe is not empty, otherwise it throws a <code>DequePopException</code> + * + * @throws DequePopException + */ + <A extends T> A take(); + + /** + * @since 1.10 + * @return but does not removes the next element if the pipe is not empty, otherwise <code>null</code> + */ + <A extends T> A read(); + + /** + * @since 1.10 + */ + List<?> tryTakeMultiple(int numElementsToTake); + + /** + * <i>Attention: Checking for emptiness is rarely appropriate, since the state from EMPTY to NON-EMPTY can change between check and access (stale state).</i> + * + * @return <code>true</code> if this pipe is empty in this special point in time, otherwise <code>false</code>. + */ + boolean isEmpty(); + + /** + * @since 1.10 + */ + <S extends ISource, A extends T> void setSourcePort(final IOutputPort<S, A> sourcePort); + + /** + * @since 1.10 + */ + <S extends ISink<S>, A extends T> void setTargetPort(final IInputPort<S, T> targetPort); + + /** + * @since 1.10 + */ + IOutputPort<?, ? extends T> getSourcePort(); + + /** + * @since 1.10 + */ + IInputPort<?, T> getTargetPort(); + + /** + * @throws Exception + * @since 1.10 + */ + void notifyPipelineStarts() throws Exception; + + /** + * @since 1.10 + */ + void notifyPipelineStops(); + + /** + * @since 1.10 + */ + void close(); + +} diff --git a/src/main/java/teetime/framework/core/IPipeCommand.java b/src/main/java/teetime/framework/core/IPipeCommand.java new file mode 100644 index 00000000..6f06d7a2 --- /dev/null +++ b/src/main/java/teetime/framework/core/IPipeCommand.java @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IPipeCommand { + + void execute(IPipe<?> pipe) throws Exception; +} diff --git a/src/main/java/teetime/framework/core/IPipeline.java b/src/main/java/teetime/framework/core/IPipeline.java new file mode 100644 index 00000000..21372d7e --- /dev/null +++ b/src/main/java/teetime/framework/core/IPipeline.java @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +import java.util.List; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IPipeline { + + List<? extends IStage> getStages(); + + List<? extends IStage> getStartStages(); + + void fireStartNotification() throws Exception; + + void fireStopNotification(); +} diff --git a/src/main/java/teetime/framework/core/IPort.java b/src/main/java/teetime/framework/core/IPort.java new file mode 100644 index 00000000..3f9d501f --- /dev/null +++ b/src/main/java/teetime/framework/core/IPort.java @@ -0,0 +1,22 @@ +package teetime.framework.core; + +/** + * + * @author Christian Wulf + * + * @since 1.10 + * + * @param <T> + * the type of elements this port accepts + */ +interface IPort<S extends IStage, T> { + + IPipe<? super T> getAssociatedPipe(); + + <A extends T> void setAssociatedPipe(final IPipe<? super T> associatedPipe); + + S getOwningStage(); + + int getIndex(); + +} diff --git a/src/main/java/teetime/framework/core/IPortListener.java b/src/main/java/teetime/framework/core/IPortListener.java new file mode 100644 index 00000000..71b05c98 --- /dev/null +++ b/src/main/java/teetime/framework/core/IPortListener.java @@ -0,0 +1,26 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IPortListener<S extends IStage> { + + void onPortIsClosed(IInputPort<S, ?> inputPort); +} diff --git a/src/main/java/teetime/framework/core/ISink.java b/src/main/java/teetime/framework/core/ISink.java new file mode 100644 index 00000000..4eaab240 --- /dev/null +++ b/src/main/java/teetime/framework/core/ISink.java @@ -0,0 +1,30 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +/** + * + * @author Christian Wulf + * + * @since 1.10 + * + * @param <S> + */ +public interface ISink<S extends IStage> extends IStage { + + // BETTER remove the type parameter +} diff --git a/src/main/java/teetime/framework/core/ISource.java b/src/main/java/teetime/framework/core/ISource.java new file mode 100644 index 00000000..77985e87 --- /dev/null +++ b/src/main/java/teetime/framework/core/ISource.java @@ -0,0 +1,27 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +/** + * @author Christian Wulf + * + * @since 1.10 + * + */ +public interface ISource extends IStage { + +} diff --git a/src/main/java/teetime/framework/core/IStage.java b/src/main/java/teetime/framework/core/IStage.java new file mode 100644 index 00000000..bd5c09f0 --- /dev/null +++ b/src/main/java/teetime/framework/core/IStage.java @@ -0,0 +1,178 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +import java.util.Collection; +import java.util.List; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface IStage extends IBaseStage { + + public final static int DEPTH_NOT_SET = -1; + + /** + * @since 1.10 + */ + public String getId(); + + /** + * @return <code>true</code> if the execution took enough tokens from the input ports so that the stage made progress due to this execution, <code>false</code> + * otherwise. The definition of <i>progress</i> depends on the semantics of the particular stage. + * + * <p> + * Example usage: + * </p> + * + * <pre> + * <code> + * boolean execute() { + * final Tuple token = this.tryTake(FILE_WORDCOUNT_TUPLE); + * if (token == null) { + * return false; + * } + * ... + * return true; + * } + * </code> + * </pre> + * + * @since 1.10 + */ + boolean execute(); + + // Let the method uncommented for documentation purpose:<br> + // Since the execution can dependent on the state of one, a subset, or all input queues,<br> + // an overloaded version with one input port is useless. + // void execute(InputPort inputPort); + // + // + // Let the method uncommented for documentation purpose:<br> + // Since the execution can dependent on one, a subset, or all input queues,<br> + // an overloaded version with a task bundle of one single input port is useless. + // void execute(TaskBundle taskBundle); + + /** + * + * @return <code>true</code> if the stage may be disabled by the pipeline scheduler, <code>false</code> otherwise. + * + * @since 1.10 + */ + boolean mayBeDisabled(); + + /** + * @since 1.10 + */ + void fireSignalClosingToAllOutputPorts(); + + /** + * @since 1.10 + */ + void fireSignalClosingToAllInputPorts(); + + /** + * @since 1.10 + */ + void notifyPipelineStarts() throws Exception; + + /** + * @since 1.10 + */ + void notifyPipelineStops(); + + /** + * <i>Hint: Used by the scheduler</i> + * + * @since 1.10 + */ + Context<?> getContext(); + + /** + * @since 1.10 + */ + IOutputPort<?, ?> getOutputPortByIndex(int index); + + /** + * @since 1.10 + */ + IInputPort<?, ?> getInputPortByIndex(int index); + + /** + * @since 1.10 + */ + int getAccessesDeviceId(); + + /** + * @since 1.10 + */ + void setAccessesDeviceId(final int accessesDeviceId); + + /** + * <i>Hint: Only needed by stage schedulers.</i> + * + * @return + */ + public Collection<? extends IStage> getAllOutputStages(); + + /** + * @since 1.10 + */ + public <S extends IStage> List<IInputPort<S, ?>> getInputPorts(); + + /** + * @since 1.10 + */ + public <S extends IStage> List<IOutputPort<S, ?>> getOutputPorts(); + + Thread getOwningThread(); + + void setOwningThread(Thread owningThread); + + /** + * @since 1.10 + */ + long getLastDuration(); + + /** + * @since 1.10 + */ + void notifyOutputPipes(IPipeCommand pipeCommand) throws Exception; + + /** + * @since 1.10 + */ + public int getDepth(); + + /** + * @since 1.10 + */ + public void setDepth(int depth); + + /** + * @since 1.10 + */ + public int getSchedulingIndex(); + + /** + * @since 1.10 + */ + public void setSchedulingIndex(int schedulingIndex); + +} diff --git a/src/main/java/teetime/framework/core/InputPortImpl.java b/src/main/java/teetime/framework/core/InputPortImpl.java new file mode 100644 index 00000000..fb9c6c2e --- /dev/null +++ b/src/main/java/teetime/framework/core/InputPortImpl.java @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.core; + +class InputPortImpl<S extends IStage, T> extends AbstractPort<S, T> implements IInputPort<S, T> { + + private volatile State state = State.OPEN; + + private IPortListener<S> stageListener; + + public InputPortImpl(final S owningStage) { + this.setOwningStage(owningStage); + } + + public void setState(final State state) { + this.state = state; + } + + public State getState() { + return this.state; + } + + public void setPortListener(final IPortListener<S> stageListener) { + this.stageListener = stageListener; + } + + public void close() { + this.stageListener.onPortIsClosed(this); + } + +} diff --git a/src/main/java/teetime/framework/core/OutputPortImpl.java b/src/main/java/teetime/framework/core/OutputPortImpl.java new file mode 100644 index 00000000..e3c0e42d --- /dev/null +++ b/src/main/java/teetime/framework/core/OutputPortImpl.java @@ -0,0 +1,10 @@ +package teetime.framework.core; + + +public class OutputPortImpl<S extends IStage, T> extends AbstractPort<S, T> implements IOutputPort<S, T> { + + public OutputPortImpl(final S owningStage) { + this.setOwningStage(owningStage); + } + +} diff --git a/src/main/java/teetime/framework/core/Pipeline.java b/src/main/java/teetime/framework/core/Pipeline.java new file mode 100644 index 00000000..7848fa0c --- /dev/null +++ b/src/main/java/teetime/framework/core/Pipeline.java @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.core; + +import java.util.List; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class Pipeline implements IPipeline { + + private List<? extends IStage> startStages; + private List<IStage> stages; + + public List<? extends IStage> getStartStages() { + return this.startStages; + } + + public List<IStage> getStages() { + return this.stages; + } + + public void fireStartNotification() throws Exception { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStarts(); + } + } + + public void fireStopNotification() { + for (final IStage stage : this.getStartStages()) { + stage.notifyPipelineStops(); + } + } + + public void setStartStages(final List<? extends IStage> startStages) { + this.startStages = startStages; + } + + public void setStages(final List<IStage> stages) { + this.stages = stages; + } + +} diff --git a/src/main/java/teetime/framework/sequential/MethodCallPipe.java b/src/main/java/teetime/framework/sequential/MethodCallPipe.java new file mode 100644 index 00000000..cff55296 --- /dev/null +++ b/src/main/java/teetime/framework/sequential/MethodCallPipe.java @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.framework.sequential; + +import java.util.List; + +import teetime.framework.core.AbstractPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.ISink; +import teetime.framework.core.ISource; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class MethodCallPipe<T> extends AbstractPipe<T> { + + private T storedToken; + + public MethodCallPipe(final T initialToken) { + this.storedToken = initialToken; + } + + public MethodCallPipe() { + this.storedToken = null; + } + + static public <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) { + final MethodCallPipe<T> pipe = new MethodCallPipe<T>(); + pipe.setSourcePort(sourcePort); + pipe.setTargetPort(targetPort); + } + + @Override + protected void putInternal(final T token) { + this.storedToken = token; + this.getTargetPort().getOwningStage().execute(); + } + + @Override + protected T tryTakeInternal() { + final T temp = this.storedToken; + this.storedToken = null; + return temp; + } + + public T take() { + return this.tryTake(); + } + + public T read() { + return this.storedToken; + } + + public void putMultiple(final List<T> items) { + throw new IllegalStateException("Putting more than one element is not possible. You tried to put " + items.size() + " items."); + } + + public List<?> tryTakeMultiple(final int numElementsToTake) { + throw new IllegalStateException("Taking more than one element is not possible. You tried to take " + numElementsToTake + " items."); + } + + public void copyAllOtherPipes(final List<MethodCallPipe<T>> pipesOfGroup) { + // is not needed in a synchronous execution + } + + public boolean isEmpty() { + return this.storedToken == null; + } + +} diff --git a/src/main/java/teetime/framework/sequential/Pipeline.java.todo b/src/main/java/teetime/framework/sequential/Pipeline.java.todo new file mode 100644 index 00000000..9b95dc19 --- /dev/null +++ b/src/main/java/teetime/framework/sequential/Pipeline.java.todo @@ -0,0 +1,190 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package kieker.panalysis.framework.sequential; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import kieker.panalysis.framework.core.AbstractFilter; +import kieker.panalysis.framework.core.IInputPort; +import kieker.panalysis.framework.core.IOutputPort; +import kieker.panalysis.framework.core.IPipe; +import kieker.panalysis.framework.core.ISink; +import kieker.panalysis.framework.core.ISource; +import kieker.panalysis.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + * + * @param <P> + * The type of the pipes + */ +public class Pipeline<P extends IPipe<?>> { + + protected final List<IStage> stages = new LinkedList<IStage>(); + private int freeId = 0; + private AbstractFilter<?>[] startStages; + + private P currentPipe; + private final Map<Integer, List<P>> pipeGroups; + private final Map<IOutputPort<?, ?>, IInputPort<?, ?>> connections = new HashMap<IOutputPort<?, ?>, IInputPort<?, ?>>(); + + /** + * The default constructor.<br> + * <i>Public constructors are available via the static method <code>create(..)</code></i> + * + * @see for example, {@link #create(Map)} + * + * @since 1.10 + */ + private Pipeline() { + this(new HashMap<Integer, List<P>>()); + } + + /** + * @since 1.10 + */ + private Pipeline(final Map<Integer, List<P>> pipeGroups) { + this.pipeGroups = pipeGroups; + } + + /** + * @since 1.10 + */ + // this constructor prevents the programmer from repeating the type argument + public static <P extends IPipe<?>> Pipeline<P> create() { + return new Pipeline<P>(); + } + + /** + * + * @param pipeGroups + * a map where its keys represent the group id and its values represent the list of pipes + * @return + */ + // this constructor prevents the programmer from repeating the type argument + public static <P extends IPipe<?>> Pipeline<P> create(final Map<Integer, List<P>> pipeGroups) { + return new Pipeline<P>(pipeGroups); + } + + /** + * <p> + * Adds the given <code>stage</code> to this pipeline and set a unique identifier. + * </p> + * <p> + * Use this method as indicated in the following example: <blockquote> <code>Distributor distributor = pipeline.addStage(new Distributor())</code> </blockquote> + * </p> + * + * @param stage + * @return + */ + public <S extends IStage> S addStage(final S stage) { + stage.setId(this.freeId++); + stage.setOwningPipeline(this); + this.stages.add(stage); + return stage; + } + + /** + * @since 1.10 + */ + public void start() { + this.fireStartEvent(); + + if (this.startStages.length == 0) { + throw new IllegalStateException("You need to define at least one start stage."); + } else if (this.startStages.length == 1) { + // this.startStages.get(0).execute(); + } else { + // TODO create a single distributor stage as start stage + throw new IllegalStateException("Multiple start stages are not yet supported."); + } + } + + /** + * @since 1.10 + */ + private void fireStartEvent() { + for (final IStage stage : this.stages) { + stage.onPipelineStarts(); + } + } + + /** + * @since 1.10 + */ + public List<IStage> getStages() { + return this.stages; + } + + /** + * @since 1.10 + */ + public Pipeline<P> add(final P pipe) { + this.currentPipe = pipe; + return this; + } + + /** + * @since 1.10 + */ + public <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, T> sourcePort, final IInputPort<S1, T> targetPort) { + this.connections.put(sourcePort, targetPort); + } + + /** + * @since 1.10 + */ + public void toGroup(final int pipeGroupIdentifier) { + List<P> pipes = this.pipeGroups.get(pipeGroupIdentifier); + if (pipes == null) { + pipes = new LinkedList<P>(); + this.pipeGroups.put(pipeGroupIdentifier, pipes); + } + pipes.add(this.currentPipe); + } + + /** + * @since 1.10 + */ + // BETTER move this method out of the pipeline + public void connectPipeGroups() { + for (final List<P> samePipes : this.pipeGroups.values()) { + for (final P pipe : samePipes) { + pipe.copyAllOtherPipes(samePipes); + } + } + } + + /** + * @since 1.10 + */ + public void setStartStages(final AbstractFilter<?>... startStages) { + this.startStages = startStages; + } + + /** + * @since 1.10 + */ + public AbstractFilter<?>[] getStartStages() { + return this.startStages; + } +} diff --git a/src/main/java/teetime/framework/sequential/QueuePipe.java b/src/main/java/teetime/framework/sequential/QueuePipe.java new file mode 100644 index 00000000..8543ea17 --- /dev/null +++ b/src/main/java/teetime/framework/sequential/QueuePipe.java @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.sequential; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +import teetime.framework.core.AbstractPipe; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.ISink; +import teetime.framework.core.ISource; +import teetime.util.concurrent.workstealing.CircularWorkStealingDeque; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class QueuePipe<T> extends AbstractPipe<T> { + + private final Queue<T> queue = new LinkedList<T>(); + + static public <S0 extends ISource, S1 extends ISink<S1>, T> void connect(final IOutputPort<S0, ? extends T> sourcePort, final IInputPort<S1, T> targetPort) { + final QueuePipe<T> pipe = new QueuePipe<T>(); + pipe.setSourcePort(sourcePort); + pipe.setTargetPort(targetPort); + } + + @Override + public void putInternal(final T element) { + this.queue.add(element); + } + + public void putMultiple(final List<T> elements) { + this.queue.addAll(elements); + } + + @Override + public T tryTakeInternal() { + return this.queue.poll(); + } + + public T take() { + final T element = this.tryTake(); + if (element == null) { + throw CircularWorkStealingDeque.DEQUE_IS_EMPTY_EXCEPTION; + } + return element; + } + + public T read() { + return this.queue.peek(); + } + + public List<?> tryTakeMultiple(final int numElementsToTake) { + throw new IllegalStateException("Taking more than one element is not possible. You tried to take " + numElementsToTake + " items."); + } + + public boolean isEmpty() { + return this.queue.isEmpty(); + } + +} diff --git a/src/main/java/teetime/framework/util/BaseStage2StageExtractor.java b/src/main/java/teetime/framework/util/BaseStage2StageExtractor.java new file mode 100644 index 00000000..819de274 --- /dev/null +++ b/src/main/java/teetime/framework/util/BaseStage2StageExtractor.java @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.util; + +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.core.CompositeFilter; +import teetime.framework.core.IBaseStage; +import teetime.framework.core.IStage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class BaseStage2StageExtractor { + + public List<? extends IStage> extract(final IBaseStage baseStage) { + final List<IStage> stages = new LinkedList<IStage>(); + this.extractLocally(baseStage, stages); + return stages; + } + + private void extractLocally(final IBaseStage baseStage, final List<IStage> stages) { + if (baseStage instanceof IStage) { + stages.add((IStage) baseStage); + } else if (baseStage instanceof CompositeFilter) { + final List<IBaseStage> schedulableStages = ((CompositeFilter) baseStage).getSchedulableStages(); + for (final IBaseStage childBaseStage : schedulableStages) { + this.extractLocally(childBaseStage, stages); + } + } + } + +} diff --git a/src/main/java/teetime/framework/util/Cloner.java b/src/main/java/teetime/framework/util/Cloner.java new file mode 100644 index 00000000..d8463dbe --- /dev/null +++ b/src/main/java/teetime/framework/util/Cloner.java @@ -0,0 +1,128 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.framework.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * A helper class to clone bean like objects with default constructors, getters, and setters. + * + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class Cloner { + + private Cloner() {} + + /** + * Clones the given object by creating a new instance of the class. The method invokes all getter methods on the original and stores the resulting values in the + * clone by invoking the corresponding setter methods. Properties without setter or getter methods are ignored. The resulting clone is always a new instance, + * hence original != clone holds. It is assumed that the given instance provides a default constructor without any parameters. + * + * @param original + * The original instance to clone. + * + * @return A clone of the given instance. + * + * @throws Exception + * If the given instance does not provide a default constructor, the getters or setters are not accessible, the getters and setters are not + * compatible, or any of the methods threw an exception. + */ + @SuppressWarnings("unchecked") + public static <T> T cloneObject(final T original) throws Exception { + // Comment for 1.7: The three exceptions, thrown by this called method, have the same parent exception since 1.7 + final T clone = (T) original.getClass().newInstance(); + + Cloner.cloneAllProperties(original, clone); + + return clone; + } + + private static <T> void cloneAllProperties(final T original, final T clone) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException { + final Collection<GetterSetterPair> getterSetterPairs = Cloner.getGetterSetterPairs(original.getClass()); + + for (final GetterSetterPair getterSetterPair : getterSetterPairs) { + final Object value = getterSetterPair.getGetter().invoke(original); + getterSetterPair.getSetter().invoke(clone, value); + } + } + + private static Collection<GetterSetterPair> getGetterSetterPairs(final Class<?> clazz) { + final Collection<GetterSetterPair> properties = new ArrayList<GetterSetterPair>(); + + final Map<String, Method> foundGetters = new HashMap<String, Method>(); + final Map<String, Method> foundSetters = new HashMap<String, Method>(); + + final Method[] methods = clazz.getMethods(); + for (final Method method : methods) { + if (Cloner.isGetter(method)) { + final String propertyName = method.getName().substring(3); + if (foundSetters.containsKey(propertyName)) { + properties.add(new GetterSetterPair(method, foundSetters.remove(propertyName))); + } else { + foundGetters.put(propertyName, method); + } + } else if (Cloner.isSetter(method)) { + final String propertyName = method.getName().substring(3); + if (foundGetters.containsKey(propertyName)) { + properties.add(new GetterSetterPair(foundGetters.remove(propertyName), method)); + } else { + foundSetters.put(propertyName, method); + } + } + } + + return properties; + } + + private static boolean isSetter(final Method method) { + return ((method.getReturnType() == Void.TYPE) && (method.getParameterTypes().length == 1) && (Modifier.isPublic(method.getModifiers())) && method.getName() + .matches("set[A-Z].*")); + } + + private static boolean isGetter(final Method method) { + return ((method.getReturnType() != Void.TYPE) && (method.getParameterTypes().length == 0) && (Modifier.isPublic(method.getModifiers())) && method.getName() + .matches("get[A-Z].*")); + } + + private static class GetterSetterPair { + + private final Method getter; + private final Method setter; + + public GetterSetterPair(final Method getter, final Method setter) { + this.getter = getter; + this.setter = setter; + } + + public Method getGetter() { + return this.getter; + } + + public Method getSetter() { + return this.setter; + } + + } + +} diff --git a/src/main/java/teetime/stage/Cache.java b/src/main/java/teetime/stage/Cache.java new file mode 100644 index 00000000..723f8cd9 --- /dev/null +++ b/src/main/java/teetime/stage/Cache.java @@ -0,0 +1,90 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import java.util.LinkedList; +import java.util.List; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class Cache<T> extends AbstractFilter<Cache<T>> { + + private static enum OperationMode { + CACHING, SENDING + } + + private OperationMode operationMode = OperationMode.CACHING; + + public final IInputPort<Cache<T>, T> objectInputPort = this.createInputPort(); + public final IInputPort<Cache<T>, Boolean> sendInputPort = this.createInputPort(); + + public final IOutputPort<Cache<T>, T> objectOutputPort = this.createOutputPort(); + + private final List<T> cachedObjects = new LinkedList<T>(); + + @Override + protected boolean execute(final Context<Cache<T>> context) { + if (this.operationMode == OperationMode.CACHING) { + final T object = context.tryTake(this.objectInputPort); + if (object == null) { + final Boolean shouldSend = context.tryTake(this.sendInputPort); + if (shouldSend == null) { + return false; + } else { + this.operationMode = OperationMode.SENDING; + } + } else { + this.cache(object); + } + } else { + final boolean sent = this.send(context); + if (!sent) { + this.operationMode = OperationMode.CACHING; + } + } + + return true; + } + + /** + * @since 1.10 + */ + private boolean send(final Context<Cache<T>> context) { + if (this.cachedObjects.size() > 0) { + final T cachedObject = this.cachedObjects.remove(0); + context.put(this.objectOutputPort, cachedObject); + return true; + } else { + return false; + } + } + + /** + * @since 1.10 + */ + private void cache(final T object) { + this.cachedObjects.add(object); + } + +} diff --git a/src/main/java/teetime/stage/Clock.java b/src/main/java/teetime/stage/Clock.java new file mode 100644 index 00000000..841a2438 --- /dev/null +++ b/src/main/java/teetime/stage/Clock.java @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class Clock extends AbstractFilter<Clock> { + + public final IOutputPort<Clock, Long> timestampOutputPort = this.createOutputPort(); + + @Override + protected boolean execute(final Context<Clock> context) { + final long timestamp = this.getTimestamp(); + context.put(this.timestampOutputPort, timestamp); + return true; + } + + /** + * @since 1.10 + */ + private long getTimestamp() { + return System.nanoTime(); + } + +} diff --git a/src/main/java/teetime/stage/CollectorSink.java b/src/main/java/teetime/stage/CollectorSink.java new file mode 100644 index 00000000..69206195 --- /dev/null +++ b/src/main/java/teetime/stage/CollectorSink.java @@ -0,0 +1,67 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import java.util.Collection; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class CollectorSink<T> extends AbstractFilter<CollectorSink<T>> { + + private static final int THRESHOLD = 10000; + + public final IInputPort<CollectorSink<T>, T> objectInputPort = this.createInputPort(); + private Collection<T> objects; + + public CollectorSink(final Collection<T> collection) { + this.objects = collection; + } + + public CollectorSink() { + super(); + } + + @Override + protected boolean execute(final Context<CollectorSink<T>> context) { + final T object = context.tryTake(this.objectInputPort); + if (object == null) { + return false; + } + + this.objects.add(object); + if ((this.objects.size() % THRESHOLD) == 0) { + System.out.println("size: " + this.objects.size()); + } + + return true; + } + + public Collection<T> getObjects() { + return this.objects; + } + + public void setObjects(final Collection<T> objects) { + this.objects = objects; + } + +} diff --git a/src/main/java/teetime/stage/CountingFilter.java b/src/main/java/teetime/stage/CountingFilter.java new file mode 100644 index 00000000..018227ac --- /dev/null +++ b/src/main/java/teetime/stage/CountingFilter.java @@ -0,0 +1,58 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class CountingFilter<T> extends AbstractFilter<CountingFilter<T>> { + + public final IInputPort<CountingFilter<T>, T> INPUT_OBJECT = this.createInputPort(); + public final IInputPort<CountingFilter<T>, Long> CURRENT_COUNT = this.createInputPort(); + + public final IOutputPort<CountingFilter<T>, T> RELAYED_OBJECT = this.createOutputPort(); + public final IOutputPort<CountingFilter<T>, Long> NEW_COUNT = this.createOutputPort(); + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<CountingFilter<T>> context) { + final T inputObject = context.tryTake(this.INPUT_OBJECT); + if (inputObject == null) { + return false; + } + final Long currentCount = context.tryTake(this.CURRENT_COUNT); + if (currentCount == null) { + return false; + } + + context.put(this.RELAYED_OBJECT, inputObject); + // FIXME using a MethodCallPipe leads to a stackoverflow since the value (currentCount+1) is put and taken infinitely + context.put(this.NEW_COUNT, currentCount + 1); // BETTER support pipes with primitive values to improve performance by avoiding auto-boxing + + return true; + } + +} diff --git a/src/main/java/teetime/stage/FileExtensionFilter.java b/src/main/java/teetime/stage/FileExtensionFilter.java new file mode 100644 index 00000000..329c3e72 --- /dev/null +++ b/src/main/java/teetime/stage/FileExtensionFilter.java @@ -0,0 +1,78 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import java.io.File; +import java.util.HashMap; +import java.util.regex.Pattern; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class FileExtensionFilter extends AbstractFilter<FileExtensionFilter> { + + public final IInputPort<FileExtensionFilter, File> fileInputPort = this.createInputPort(); + + public final IOutputPort<FileExtensionFilter, File> unknownFileExtensionOutputPort = this.createOutputPort(); + + private final HashMap<String, IOutputPort<FileExtensionFilter, File>> fileExtensionMap = new HashMap<String, IOutputPort<FileExtensionFilter, File>>(); + + private static final Pattern DOT_PATTERN = Pattern.compile("\\."); + + @Override + protected boolean execute(final Context<FileExtensionFilter> context) { + final File file = context.tryTake(this.fileInputPort); + if (file == null) { + return false; + } + + final String[] filenameParts = DOT_PATTERN.split(file.getName()); + final String fileExtension = filenameParts[filenameParts.length - 1]; + final IOutputPort<FileExtensionFilter, File> outputPort = this.fileExtensionMap.get(fileExtension); + if (outputPort != null) { + context.put(outputPort, file); + } else { + context.put(this.unknownFileExtensionOutputPort, file); + } + + return true; + } + + /** + * + * @param fileExtension + * the file extension (<i>a leading dot is removed automatically)</i> + * @return + */ + public IOutputPort<FileExtensionFilter, File> createOutputPortForFileExtension(String fileExtension) { + if (fileExtension.startsWith(".")) { + fileExtension = fileExtension.substring(1); + } + IOutputPort<FileExtensionFilter, File> outputPort = this.fileExtensionMap.get(fileExtension); + if (outputPort == null) { + outputPort = super.createOutputPort(); + this.fileExtensionMap.put(fileExtension, outputPort); + } + return outputPort; + } +} diff --git a/src/main/java/teetime/stage/InstanceOfFilter.java b/src/main/java/teetime/stage/InstanceOfFilter.java new file mode 100644 index 00000000..1fdb4f13 --- /dev/null +++ b/src/main/java/teetime/stage/InstanceOfFilter.java @@ -0,0 +1,53 @@ +package teetime.stage; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Jan Waller, Nils Christian Ehmke, Christian Wulf + * + * @since 1.10 + */ +public class InstanceOfFilter<I, O> extends AbstractFilter<InstanceOfFilter<I, O>> { + + public final IInputPort<InstanceOfFilter<I, O>, I> inputPort = this.createInputPort(); + + public final IOutputPort<InstanceOfFilter<I, O>, O> matchingOutputPort = this.createOutputPort(); + public final IOutputPort<InstanceOfFilter<I, O>, I> mismatchingOutputPort = this.createOutputPort(); + + private Class<O> type; + + /** + * @since 1.10 + */ + public InstanceOfFilter(final Class<O> type) { + this.type = type; + } + + @Override + protected boolean execute(final Context<InstanceOfFilter<I, O>> context) { + final I inputObject = context.tryTake(this.inputPort); + if (inputObject == null) { + return false; + } + + if (this.type.isInstance(inputObject)) { + context.put(this.matchingOutputPort, this.type.cast(inputObject)); + } else { + context.put(this.mismatchingOutputPort, inputObject); + } + + return true; + } + + public Class<O> getType() { + return this.type; + } + + public void setType(final Class<O> type) { + this.type = type; + } + +} diff --git a/src/main/java/teetime/stage/MappingException.java b/src/main/java/teetime/stage/MappingException.java new file mode 100644 index 00000000..49fd0fc3 --- /dev/null +++ b/src/main/java/teetime/stage/MappingException.java @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class MappingException extends Exception { + + private static final long serialVersionUID = 7300752837946139350L; + + public MappingException(final String text) { + super(text); + } + +} diff --git a/src/main/java/teetime/stage/NoopFilter.java b/src/main/java/teetime/stage/NoopFilter.java new file mode 100644 index 00000000..de1b31b4 --- /dev/null +++ b/src/main/java/teetime/stage/NoopFilter.java @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class NoopFilter<T> extends AbstractFilter<NoopFilter<T>> { + + public final IInputPort<NoopFilter<T>, T> inputPort = this.createInputPort(); + public final IOutputPort<NoopFilter<T>, T> outputPort = this.createOutputPort(); + + @Override + protected boolean execute(final Context<NoopFilter<T>> context) { + final T inputElement = context.tryTake(this.inputPort); + if (inputElement == null) { + return false; + } + + context.put(this.outputPort, inputElement); + + return true; + } + +} diff --git a/src/main/java/teetime/stage/StartTimestampFilter.java b/src/main/java/teetime/stage/StartTimestampFilter.java new file mode 100644 index 00000000..327e5e19 --- /dev/null +++ b/src/main/java/teetime/stage/StartTimestampFilter.java @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import teetime.examples.throughput.TimestampObject; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StartTimestampFilter extends AbstractFilter<StartTimestampFilter> { + + public final IInputPort<StartTimestampFilter, TimestampObject> inputPort = this.createInputPort(); + public final IOutputPort<StartTimestampFilter, TimestampObject> outputPort = this.createOutputPort(); + + @Override + protected boolean execute(final Context<StartTimestampFilter> context) { + final TimestampObject inputObject = context.tryTake(this.inputPort); + if (inputObject == null) { + return false; + } + + inputObject.setStartTimestamp(System.nanoTime()); + + context.put(this.outputPort, inputObject); + + return true; + } + +} diff --git a/src/main/java/teetime/stage/StopTimestampFilter.java b/src/main/java/teetime/stage/StopTimestampFilter.java new file mode 100644 index 00000000..fc65ca63 --- /dev/null +++ b/src/main/java/teetime/stage/StopTimestampFilter.java @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage; + +import teetime.examples.throughput.TimestampObject; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StopTimestampFilter extends AbstractFilter<StopTimestampFilter> { + + public final IInputPort<StopTimestampFilter, TimestampObject> inputPort = this.createInputPort(); + public final IOutputPort<StopTimestampFilter, TimestampObject> outputPort = this.createOutputPort(); + + @Override + protected boolean execute(final Context<StopTimestampFilter> context) { + final TimestampObject inputObject = context.tryTake(this.inputPort); + if (inputObject == null) { + return false; + } + + inputObject.setStopTimestamp(System.nanoTime()); + + context.put(this.outputPort, inputObject); + + return true; + } + +} diff --git a/src/main/java/teetime/stage/SuperTypeFilter.java b/src/main/java/teetime/stage/SuperTypeFilter.java new file mode 100644 index 00000000..8482233c --- /dev/null +++ b/src/main/java/teetime/stage/SuperTypeFilter.java @@ -0,0 +1,23 @@ +package teetime.stage; + +import java.util.Collection; + +import teetime.stage.predicate.IsSuperTypePredicate; +import teetime.stage.predicate.PredicateFilter; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class SuperTypeFilter<T> extends PredicateFilter<T> { + + /** + * @param acceptedClasses + * @since 1.10 + */ + public SuperTypeFilter(final Collection<Class<?>> acceptedClasses) { + super(new IsSuperTypePredicate<T>(acceptedClasses)); + } + +} diff --git a/src/main/java/teetime/stage/TypeLoggerFilter.java b/src/main/java/teetime/stage/TypeLoggerFilter.java new file mode 100644 index 00000000..304579a5 --- /dev/null +++ b/src/main/java/teetime/stage/TypeLoggerFilter.java @@ -0,0 +1,71 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage; + +import java.io.PrintStream; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Matthias Rohr, Jan Waller, Nils Christian Ehmke, Christian Wulf + * + * @since 1.10 + */ +public class TypeLoggerFilter<T> extends AbstractFilter<TypeLoggerFilter<T>> { + + public final IInputPort<TypeLoggerFilter<T>, T> INPUT_OBJECT = this.createInputPort(); + + public final IOutputPort<TypeLoggerFilter<T>, T> RELAYED_OBJECT = this.createOutputPort(); + + private final PrintStream printStream; + + /** + * @since 1.10 + */ + private TypeLoggerFilter() { + this.printStream = System.out; + } + + /** + * @since 1.10 + */ + public static <T> TypeLoggerFilter<T> create() { + return new TypeLoggerFilter<T>(); + } + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<TypeLoggerFilter<T>> context) { + final T inputObject = context.tryTake(this.INPUT_OBJECT); + if (inputObject == null) { + return false; + } + + final StringBuilder sb = new StringBuilder(128); + sb.append(super.getId()).append('(').append(inputObject.getClass().getSimpleName()).append(") ").append(inputObject.toString()); + this.printStream.println(sb.toString()); + + context.put(this.RELAYED_OBJECT, inputObject); + return true; + } + +} diff --git a/src/main/java/teetime/stage/basic/Delay.java b/src/main/java/teetime/stage/basic/Delay.java new file mode 100644 index 00000000..d878e5f1 --- /dev/null +++ b/src/main/java/teetime/stage/basic/Delay.java @@ -0,0 +1,73 @@ +package teetime.stage.basic; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * + * @author Christian Wulf + * + * @since 1.10 + * + * @param <T> + */ +public class Delay<T> extends AbstractFilter<Delay<T>> { + + public final IInputPort<Delay<T>, T> INPUT_OBJECT = this.createInputPort(); + + public final IOutputPort<Delay<T>, T> RELAYED_OBJECT = this.createOutputPort(); + + private long initialDelayInNs; + private long intervalDelayInNs; + + private boolean initialDelayExceeded = false; + private long lastTimeInNs = this.getCurrentTimeInNs(); + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<Delay<T>> context) { + final long passedTimeInNs = this.getCurrentTimeInNs() - this.lastTimeInNs; + final long thresholdInNs = (this.initialDelayExceeded) ? this.intervalDelayInNs : this.initialDelayInNs; + + if (passedTimeInNs >= thresholdInNs) { + final T token = context.tryTake(this.INPUT_OBJECT); + if (token == null) { + return false; + } + + this.lastTimeInNs += thresholdInNs; + context.put(this.RELAYED_OBJECT, token); + + this.initialDelayExceeded = true; + } + + return true; + } + + /** + * @since 1.10 + */ + private long getCurrentTimeInNs() { + return System.nanoTime(); + } + + public long getInitialDelayInNs() { + return initialDelayInNs; + } + + public void setInitialDelayInNs(long initialDelayInNs) { + this.initialDelayInNs = initialDelayInNs; + } + + public long getIntervalDelayInNs() { + return intervalDelayInNs; + } + + public void setIntervalDelayInNs(long intervalDelayInNs) { + this.intervalDelayInNs = intervalDelayInNs; + } +} diff --git a/src/main/java/teetime/stage/basic/ObjectProducer.java b/src/main/java/teetime/stage/basic/ObjectProducer.java new file mode 100644 index 00000000..c94c6397 --- /dev/null +++ b/src/main/java/teetime/stage/basic/ObjectProducer.java @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic; + +import java.util.concurrent.Callable; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ObjectProducer<T> extends AbstractFilter<ObjectProducer<T>> { + + public final IOutputPort<ObjectProducer<T>, T> outputPort = this.createOutputPort(); + + private Callable<T> objectCreator; + private long numObjectsToCreate; + + /** + * @since 1.10 + */ + public ObjectProducer(final long numObjectsToCreate, final Callable<T> objectCreator) { + this.numObjectsToCreate = numObjectsToCreate; + this.objectCreator = objectCreator; + } + + /** + * @since 1.10 + */ + public ObjectProducer() { + super(); + } + + @Override + protected boolean execute(final Context<ObjectProducer<T>> context) { + if (this.numObjectsToCreate == 0) { + return false; + } + + try { + final T newObject = this.objectCreator.call(); + context.put(this.outputPort, newObject); + } catch (final Exception e) { + throw new IllegalStateException(); + } + + this.numObjectsToCreate--; + + return true; + } + + public Callable<T> getObjectCreator() { + return this.objectCreator; + } + + public void setObjectCreator(final Callable<T> objectCreator) { + this.objectCreator = objectCreator; + } + + public long getNumObjectsToCreate() { + return this.numObjectsToCreate; + } + + public void setNumObjectsToCreate(final long numObjectsToCreate) { + this.numObjectsToCreate = numObjectsToCreate; + } + +} diff --git a/src/main/java/teetime/stage/basic/RepeaterSource.java b/src/main/java/teetime/stage/basic/RepeaterSource.java new file mode 100644 index 00000000..495befe8 --- /dev/null +++ b/src/main/java/teetime/stage/basic/RepeaterSource.java @@ -0,0 +1,75 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.basic; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class RepeaterSource<T> extends AbstractFilter<RepeaterSource<T>> { + + public final IInputPort<RepeaterSource<T>, Boolean> START = this.createInputPort(); + + public final IOutputPort<RepeaterSource<T>, T> OUTPUT = this.createOutputPort(); + + private final T outputRecord; + private final int num; + + /** + * @since 1.10 + * @param outputRecord + * @param num + */ + private RepeaterSource(final T outputRecord, final int num) { + this.outputRecord = outputRecord; + this.num = num; + } + + /** + * @since 1.10 + * @param outputRecord + * @param num + * @return + */ + // this constructor prevents the programmer from repeating the type argument + public static <T> RepeaterSource<T> create(final T outputRecord, final int num) { + return new RepeaterSource<T>(outputRecord, num); + } + + @Override + protected boolean execute(final Context<RepeaterSource<T>> context) { + final Boolean token = context.tryTake(this.START); + if (token == null) { + return false; + } + + // TODO consider to put only one record per iteration and decrement the num directly + int counter = this.num; + while (counter-- > 0) { + context.put(this.OUTPUT, this.outputRecord); + } + + return true; + } + +} diff --git a/src/main/java/teetime/stage/basic/distributor/CloneStrategy.java b/src/main/java/teetime/stage/basic/distributor/CloneStrategy.java new file mode 100644 index 00000000..e1687a07 --- /dev/null +++ b/src/main/java/teetime/stage/basic/distributor/CloneStrategy.java @@ -0,0 +1,34 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic.distributor; + +import java.util.List; + +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public final class CloneStrategy<T> implements IDistributorStrategy<T> { + + public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> outputPorts, final T input) { + throw new UnsupportedOperationException(); + } + +} diff --git a/src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java b/src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java new file mode 100644 index 00000000..928f99f7 --- /dev/null +++ b/src/main/java/teetime/stage/basic/distributor/CopyByReferenceStrategy.java @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic.distributor; + +import java.util.List; + +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public final class CopyByReferenceStrategy<T> implements IDistributorStrategy<T> { + + @SuppressWarnings("unchecked") + public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> outputPorts, final T input) { + for (final IOutputPort<S, ?> port : outputPorts) { + context.put((IOutputPort<S, T>) port, input); + } + + return true; + } + +} diff --git a/src/main/java/teetime/stage/basic/distributor/Distributor.java b/src/main/java/teetime/stage/basic/distributor/Distributor.java new file mode 100644 index 00000000..5b0a8ea7 --- /dev/null +++ b/src/main/java/teetime/stage/basic/distributor/Distributor.java @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.basic.distributor; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + * + * @param T + * the type of the input port and the output ports + */ +public class Distributor<T> extends AbstractFilter<Distributor<T>> { + + public final IInputPort<Distributor<T>, T> genericInputPort = this.createInputPort(); + + private IDistributorStrategy<T> strategy = new RoundRobinStrategy<T>(); + + public IDistributorStrategy<T> getStrategy() { + return this.strategy; + } + + public void setStrategy(final IDistributorStrategy<T> strategy) { + this.strategy = strategy; + } + + @Override + protected boolean execute(final Context<Distributor<T>> context) { + final T object = context.tryTake(this.genericInputPort); + if (object == null) { + return false; + } + + return this.strategy.distribute(context, this.getOutputPorts(), object); + } + + public IOutputPort<Distributor<T>, T> getNewOutputPort() { + final IOutputPort<Distributor<T>, T> newOutputPort = this.createOutputPort(); + return newOutputPort; + } +} diff --git a/src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java b/src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java new file mode 100644 index 00000000..cb359235 --- /dev/null +++ b/src/main/java/teetime/stage/basic/distributor/IDistributorStrategy.java @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic.distributor; + +import java.util.List; + +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public interface IDistributorStrategy<T> { + + public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> allOutputPorts, final T input); + +} diff --git a/src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java b/src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java new file mode 100644 index 00000000..028e4f82 --- /dev/null +++ b/src/main/java/teetime/stage/basic/distributor/RoundRobinStrategy.java @@ -0,0 +1,48 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic.distributor; + +import java.util.List; + +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public final class RoundRobinStrategy<T> implements IDistributorStrategy<T> { + + private int index = 0; + + public <S extends Distributor<T>> boolean distribute(final Context<S> context, final List<IOutputPort<S, ?>> outputPorts, final T object) { + final IOutputPort<S, T> port = this.getNextPortInRoundRobinOrder(outputPorts); + context.put(port, object); + + return true; + } + + @SuppressWarnings("unchecked") + private <S extends Distributor<T>> IOutputPort<S, T> getNextPortInRoundRobinOrder(final List<IOutputPort<S, ?>> outputPorts) { + final IOutputPort<S, T> port = (IOutputPort<S, T>) outputPorts.get(this.index); + + this.index = (this.index + 1) % outputPorts.size(); + + return port; + } + +} diff --git a/src/main/java/teetime/stage/basic/merger/IMergerStrategy.java b/src/main/java/teetime/stage/basic/merger/IMergerStrategy.java new file mode 100644 index 00000000..390679b6 --- /dev/null +++ b/src/main/java/teetime/stage/basic/merger/IMergerStrategy.java @@ -0,0 +1,32 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic.merger; + +import java.util.List; + +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public interface IMergerStrategy<T> { + + public <S extends Merger<T>> T getNextInput(final Context<S> context, final List<IInputPort<S, ?>> inputPorts); + +} diff --git a/src/main/java/teetime/stage/basic/merger/Merger.java b/src/main/java/teetime/stage/basic/merger/Merger.java new file mode 100644 index 00000000..0a26fecb --- /dev/null +++ b/src/main/java/teetime/stage/basic/merger/Merger.java @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.basic.merger; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.Description; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * + * @author Christian Wulf + * + * @since 1.10 + * + * @param <T> + * the type of the input ports and the output port + */ +@Description("This stage merges data from the input ports, by taking elements according to the chosen merge strategy and by putting them to the output port.") +public class Merger<T> extends AbstractFilter<Merger<T>> { + + public final IOutputPort<Merger<T>, T> outputPort = this.createOutputPort(); + + private IMergerStrategy<T> strategy = new RoundRobinStrategy<T>(); + + public IMergerStrategy<T> getStrategy() { + return this.strategy; + } + + public void setStrategy(final IMergerStrategy<T> strategy) { + this.strategy = strategy; + } + + @Override + protected boolean execute(final Context<Merger<T>> context) { + final T token = this.strategy.getNextInput(context, this.getInputPorts()); + if (token == null) { + return false; + } + + context.put(this.outputPort, token); + return true; + } + + public IInputPort<Merger<T>, T> getNewInputPort() { + final IInputPort<Merger<T>, T> newInputPort = this.createInputPort(); + return newInputPort; + } + +} diff --git a/src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java b/src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java new file mode 100644 index 00000000..db8c674f --- /dev/null +++ b/src/main/java/teetime/stage/basic/merger/RoundRobinStrategy.java @@ -0,0 +1,54 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.basic.merger; + +import java.util.List; + +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public final class RoundRobinStrategy<T> implements IMergerStrategy<T> { + + private int index = 0; + + public <S extends Merger<T>> T getNextInput(final Context<S> context, final List<IInputPort<S, ?>> inputPorts) { + int size = inputPorts.size(); + // check each port at most once to avoid a potentially infinite loop + while (size-- > 0) { + final IInputPort<S, T> inputPort = this.getNextPortInRoundRobinOrder(inputPorts); + final T token = context.tryTake(inputPort); + if (token != null) { + return token; + } + } + return null; + } + + @SuppressWarnings("unchecked") + private <S extends Merger<T>> IInputPort<S, T> getNextPortInRoundRobinOrder(final List<IInputPort<S, ?>> inputPorts) { + final IInputPort<S, T> port = (IInputPort<S, T>) inputPorts.get(this.index); + + this.index = (this.index + 1) % inputPorts.size(); + + return port; + } + +} diff --git a/src/main/java/teetime/stage/composite/CycledCountingFilter.java b/src/main/java/teetime/stage/composite/CycledCountingFilter.java new file mode 100644 index 00000000..ab715eef --- /dev/null +++ b/src/main/java/teetime/stage/composite/CycledCountingFilter.java @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.composite; + +import teetime.framework.core.IPipe; +import teetime.stage.CountingFilter; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class CycledCountingFilter<T> extends CountingFilter<T> { + + // BETTER hide the internal ports; the following uncommented lines do not work however + // private final IInputPort<CountingFilter<T>, Long> CURRENT_COUNT = this.createInputPort(); + // private final IOutputPort<CountingFilter<T>, Long> NEW_COUNT = this.createOutputPort(); + + /** + * @since 1.10 + * @param countingPipe + */ + // FIXME a non-default constructor is not allowed + private CycledCountingFilter(final IPipe<Long> countingPipe) { + countingPipe.setSourcePort(this.NEW_COUNT); + countingPipe.setTargetPort(this.CURRENT_COUNT); + // FIXME counting pipe needs to be added to a group + } + + // this constructor prevents the programmer from repeating the type argument + public static <T> CycledCountingFilter<T> create(final IPipe<Long> countingPipe) { + return new CycledCountingFilter<T>(countingPipe); + } + + /** + * @since 1.10 + * @return + */ + public Long getCurrentCount() { + return this.getContext().read(this.CURRENT_COUNT); + } + +} diff --git a/src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java b/src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java new file mode 100644 index 00000000..dc9e397a --- /dev/null +++ b/src/main/java/teetime/stage/composite/ReadRecordFromCsvFileFilter.java @@ -0,0 +1,65 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.composite; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IPipe; +import teetime.stage.io.File2TextLinesFilter; +import teetime.stage.kieker.className.ClassNameRegistryRepository; +import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter; +import teetime.stage.util.TextLine; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +// FIXME extend from CompositeStage +public class ReadRecordFromCsvFileFilter extends AbstractFilter<File2TextLinesFilter> { + + private final File2TextLinesFilter stage0; + private final TextLine2RecordFilter stage1; + + /** + * @since 1.10 + * @param textLinePipe + */ + public ReadRecordFromCsvFileFilter(final IPipe<TextLine> textLinePipe, final ClassNameRegistryRepository classNameRegistryRepository) { + this.stage0 = new File2TextLinesFilter(); + this.stage1 = new TextLine2RecordFilter(classNameRegistryRepository); + + textLinePipe.setSourcePort(this.stage0.textLineOutputPort); + textLinePipe.setTargetPort(this.stage1.textLineInputPort); + // FIXME textLinePipe needs to be added to a group + + /* + * FIXME a composite filter only serves as a convenient way to connect multiple stages or to realize self-connected queues.<br> + * How should the stage scheduler be aware of these internal stages?<br> + * Should the context not be passed to each of the internal stages? + */ + } + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<File2TextLinesFilter> context) { + return this.stage0.execute(); + } + +} diff --git a/src/main/java/teetime/stage/composite/TeeFilter.java b/src/main/java/teetime/stage/composite/TeeFilter.java new file mode 100644 index 00000000..a249edec --- /dev/null +++ b/src/main/java/teetime/stage/composite/TeeFilter.java @@ -0,0 +1,56 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.composite; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; +import teetime.framework.core.IPipe; +import teetime.framework.sequential.MethodCallPipe; +import teetime.stage.basic.distributor.Distributor; +import teetime.stage.io.Printer; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class TeeFilter<T> extends AbstractFilter<TeeFilter<T>> { + + private final Distributor<T> stage0; + private final Printer<T> stage1; + + public TeeFilter(final IPipe<T> pipe) { + this.stage0 = new Distributor<T>(); + this.stage1 = new Printer<T>(); + + final IOutputPort<Distributor<T>, T> outputPort = this.stage0.getNewOutputPort(); + + pipe.setSourcePort(outputPort); + pipe.setTargetPort(this.stage0.genericInputPort); + + final IPipe<T> internalPipeline = new MethodCallPipe<T>(); + internalPipeline.setSourcePort(outputPort); + internalPipeline.setTargetPort(this.stage1.input); + } + + @Override + protected boolean execute(final Context<TeeFilter<T>> context) { + return this.stage0.execute(); + } + +} diff --git a/src/main/java/teetime/stage/io/DbReader.java b/src/main/java/teetime/stage/io/DbReader.java new file mode 100644 index 00000000..e252b7df --- /dev/null +++ b/src/main/java/teetime/stage/io/DbReader.java @@ -0,0 +1,186 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.io; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; + +import teetime.common.exception.MonitoringRecordException; +import teetime.common.record.AbstractMonitoringRecord; +import teetime.common.record.IMonitoringRecord; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.Description; +import teetime.framework.core.IOutputPort; + +/** + * A very simple database reader that probably only works for small data sets. + * + * @author Jan Waller, Nils Christian Ehmke + * + * @since 1.10 + */ +@Description("A reader which reads records from a database") +public class DbReader extends AbstractFilter<DbReader> { + + private final IOutputPort<DbReader, IMonitoringRecord> outputPort = super.createOutputPort(); + + @Description("The classname of the driver used for the connection.") + private String driverClassname = "org.apache.derby.jdbc.EmbeddedDrive"; + @Description("The connection string used to establish the connection.") + private String connectionString = "jdbc:derby:tmp/KIEKER;user=DBUSER;password=DBPASS"; + @Description("The prefix of the used table within the database.") + private String tablePrefix = "kieker"; + + private volatile boolean running = true; + + @Override + public void onPipelineStarts() throws Exception { + super.onPipelineStarts(); + try { + Class.forName(this.driverClassname).newInstance(); + } catch (final Exception ex) { // NOPMD NOCS (IllegalCatchCheck) + throw new Exception("DB driver registration failed. Perhaps the driver jar is missing?", ex); + } + } + + @Override + public void onPipelineStops() { + super.logger.info("Shutdown of DBReader requested."); + this.running = false; + super.onPipelineStops(); + } + + @Override + protected boolean execute(final Context<DbReader> context) { + Connection connection = null; + try { + connection = DriverManager.getConnection(this.connectionString); + Statement getIndexTable = null; + try { + getIndexTable = connection.createStatement(); + ResultSet indexTable = null; + try { // NOCS (nested try) + indexTable = getIndexTable.executeQuery("SELECT * from " + this.tablePrefix); + while (this.running && indexTable.next()) { + final String tablename = indexTable.getString(1); + final String classname = indexTable.getString(2); + try { // NOCS (nested try) + this.table2record(context, connection, tablename, AbstractMonitoringRecord.classForName(classname)); + } catch (final MonitoringRecordException ex) { + // log error but continue with next table + super.logger.error("Failed to load records of type " + classname + " from table " + tablename, ex); + continue; + } + } + } finally { + if (indexTable != null) { + indexTable.close(); + } + } + } finally { + if (getIndexTable != null) { + getIndexTable.close(); + } + } + } catch (final SQLException ex) { + super.logger.error("SQLException with SQLState: '" + ex.getSQLState() + "' and VendorError: '" + ex.getErrorCode() + "'", ex); + return false; + } finally { + if (connection != null) { + try { + connection.close(); + } catch (final SQLException ex) { + super.logger.error("SQLException with SQLState: '" + ex.getSQLState() + "' and VendorError: '" + ex.getErrorCode() + "'", ex); + } + } + } + return true; + } + + public final String getDriverClassname() { + return this.driverClassname; + } + + public final void setDriverClassname(final String driverClassname) { + this.driverClassname = driverClassname; + } + + public final String getConnectionString() { + return this.connectionString; + } + + public final void setConnectionString(final String connectionString) { + this.connectionString = connectionString; + } + + public final String getTablePrefix() { + return this.tablePrefix; + } + + public final void setTablePrefix(final String tablePrefix) { + this.tablePrefix = tablePrefix; + } + + /** + * This method uses the given table to read records and sends them to the output port. + * + * @param connection + * The connection to the database which will be used. + * @param tablename + * The name of the table containing records. + * @param clazz + * The class of the monitoring records. This will be used to convert the array into the record. + * @throws SQLException + * If something went wrong during the database access. + * @throws MonitoringRecordException + * If the data within the table could not be converted into a valid record. + */ + private void table2record(final Context<DbReader> context, final Connection connection, final String tablename, final Class<? extends IMonitoringRecord> clazz) + throws SQLException, MonitoringRecordException { + Statement selectRecord = null; + try { + selectRecord = connection.createStatement(); + ResultSet records = null; + try { + records = selectRecord.executeQuery("SELECT * from " + tablename); + final int size = records.getMetaData().getColumnCount() - 2; // remove index column + while (this.running && records.next()) { + final Object[] recordValues = new Object[size]; + for (int i = 0; i < size; i++) { + recordValues[i] = records.getObject(i + 3); + } + final IMonitoringRecord record = AbstractMonitoringRecord.createFromArray(clazz, recordValues); + record.setLoggingTimestamp(records.getLong(2)); + context.put(this.outputPort, record); + } + } finally { + if (records != null) { + records.close(); + } + } + } finally { + if (selectRecord != null) { + selectRecord.close(); + } + } + } + +} diff --git a/src/main/java/teetime/stage/io/Directory2FilesFilter.java b/src/main/java/teetime/stage/io/Directory2FilesFilter.java new file mode 100644 index 00000000..d74fa6e1 --- /dev/null +++ b/src/main/java/teetime/stage/io/Directory2FilesFilter.java @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.io; + +import java.io.File; +import java.io.FileFilter; +import java.util.Arrays; +import java.util.Comparator; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class Directory2FilesFilter extends AbstractFilter<Directory2FilesFilter> { + + public final IInputPort<Directory2FilesFilter, File> directoryInputPort = this.createInputPort(); + + public final IOutputPort<Directory2FilesFilter, File> fileOutputPort = this.createOutputPort(); + + private FileFilter filter; + private Comparator<File> fileComparator; + + /** + * @since 1.10 + */ + public Directory2FilesFilter(final FileFilter fileFilter) { + this.setFilter(fileFilter); + } + + /** + * @since 1.10 + */ + public Directory2FilesFilter(final Comparator<File> fileComparator) { + this.setFileComparator(fileComparator); + } + + /** + * @since 1.10 + */ + public Directory2FilesFilter(final FileFilter fileFilter, final Comparator<File> fileComparator) { + this.setFilter(fileFilter); + this.setFileComparator(fileComparator); + } + + /** + * @since 1.10 + */ + public Directory2FilesFilter() { + super(); + } + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<Directory2FilesFilter> context) { + final File inputDir = context.tryTake(this.directoryInputPort); + if (inputDir == null) { + return false; + } + + final File[] inputFiles = inputDir.listFiles(this.filter); + + if (inputFiles == null) { + this.logger.error("Directory '" + inputDir + "' does not exist or an I/O error occured."); + return true; + } + + if (this.fileComparator != null) { + Arrays.sort(inputFiles, this.fileComparator); + } + + for (final File file : inputFiles) { + context.put(this.fileOutputPort, file); + } + + return true; + } + + public FileFilter getFilter() { + return this.filter; + } + + public void setFilter(final FileFilter filter) { + this.filter = filter; + } + + public Comparator<File> getFileComparator() { + return this.fileComparator; + } + + public void setFileComparator(final Comparator<File> fileComparator) { + this.fileComparator = fileComparator; + } + +} diff --git a/src/main/java/teetime/stage/io/File2TextLinesFilter.java b/src/main/java/teetime/stage/io/File2TextLinesFilter.java new file mode 100644 index 00000000..114a6dcf --- /dev/null +++ b/src/main/java/teetime/stage/io/File2TextLinesFilter.java @@ -0,0 +1,89 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.io; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStreamReader; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.stage.util.TextLine; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class File2TextLinesFilter extends AbstractFilter<File2TextLinesFilter> { + + public final IInputPort<File2TextLinesFilter, File> fileInputPort = this.createInputPort(); + + public final IOutputPort<File2TextLinesFilter, TextLine> textLineOutputPort = this.createOutputPort(); + + private String charset = "UTF-8"; + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<File2TextLinesFilter> context) { + final File textFile = context.tryTake(this.fileInputPort); + if (textFile == null) { + return false; + } + + BufferedReader reader = null; + try { + reader = new BufferedReader(new InputStreamReader(new FileInputStream(textFile), this.charset)); + String line; + while ((line = reader.readLine()) != null) { + line = line.trim(); + if (line.length() != 0) { + context.put(this.textLineOutputPort, new TextLine(textFile, line)); + } // else: ignore empty line + } + } catch (final FileNotFoundException e) { + this.logger.error("", e); + } catch (final IOException e) { + this.logger.error("", e); + } finally { + try { + if (reader != null) { + reader.close(); + } + } catch (final IOException e) { + this.logger.warn("", e); + } + } + return true; + } + + public String getCharset() { + return this.charset; + } + + public void setCharset(final String charset) { + this.charset = charset; + } + +} diff --git a/src/main/java/teetime/stage/io/Printer.java b/src/main/java/teetime/stage/io/Printer.java new file mode 100644 index 00000000..538ad01c --- /dev/null +++ b/src/main/java/teetime/stage/io/Printer.java @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.io; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; +import java.io.UnsupportedEncodingException; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.Description; +import teetime.framework.core.IInputPort; + +/** + * @author Matthias Rohr, Jan Waller, Nils Christian Ehmke + * + * @since 1.10 + */ +@Description("A filter to print objects to a configured stream") +public class Printer<T> extends AbstractFilter<Printer<T>> { + + public static final String STREAM_STDOUT = "STDOUT"; + public static final String STREAM_STDERR = "STDERR"; + public static final String STREAM_STDLOG = "STDlog"; + public static final String STREAM_NULL = "NULL"; + + public static final String ENCODING_UTF8 = "UTF-8"; + + public final IInputPort<Printer<T>, T> input = this.createInputPort(); + + private PrintStream printStream; + private String streamName = STREAM_STDOUT; + private String encoding = ENCODING_UTF8; + private boolean active = true; + private boolean append = true; + + public String getStreamName() { + return this.streamName; + } + + public void setStreamName(final String streamName) { + this.streamName = streamName; + } + + public String getEncoding() { + return this.encoding; + } + + public void setEncoding(final String encoding) { + this.encoding = encoding; + } + + public boolean isAppend() { + return this.append; + } + + public void setAppend(final boolean append) { + this.append = append; + } + + @Override + public void onPipelineStarts() throws Exception { + super.onPipelineStarts(); + this.initializeStream(); + } + + @Override + public void onPipelineStops() { + this.closeStream(); + super.onPipelineStops(); + } + + private void initializeStream() { + if (STREAM_STDOUT.equals(this.streamName)) { + this.printStream = System.out; + this.active = true; + } else if (STREAM_STDERR.equals(this.streamName)) { + this.printStream = System.err; + this.active = true; + } else if (STREAM_STDLOG.equals(this.streamName)) { + this.printStream = null; + this.active = true; + } else if (STREAM_NULL.equals(this.streamName)) { + this.printStream = null; + this.active = false; + } else { + try { + this.printStream = new PrintStream(new FileOutputStream(this.streamName, this.append), false, this.encoding); + this.active = true; + } catch (final FileNotFoundException ex) { + this.active = false; + super.logger.warn("Stream could not be created", ex); + } catch (final UnsupportedEncodingException ex) { + this.active = false; + super.logger.warn("Encoding not supported", ex); + } + } + } + + private void closeStream() { + if ((this.printStream != null) && (this.printStream != System.out) && (this.printStream != System.err)) { + this.printStream.close(); + } + } + + @Override + protected boolean execute(final Context<Printer<T>> context) { + final T object = context.tryTake(this.input); + if (null == object) { + return false; + } + + if (this.active) { + final StringBuilder sb = new StringBuilder(128); + + sb.append(super.getId()); + sb.append('(').append(object.getClass().getSimpleName()).append(") ").append(object.toString()); + + final String msg = sb.toString(); + if (this.printStream != null) { + this.printStream.println(msg); + } else { + super.logger.info(msg); + } + } + + return true; + } + +} diff --git a/src/main/java/teetime/stage/io/TCPReader.java b/src/main/java/teetime/stage/io/TCPReader.java new file mode 100644 index 00000000..cac1c4fb --- /dev/null +++ b/src/main/java/teetime/stage/io/TCPReader.java @@ -0,0 +1,206 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.io; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; + +import teetime.common.exception.MonitoringRecordException; +import teetime.common.logging.Log; +import teetime.common.logging.LogFactory; +import teetime.common.record.AbstractMonitoringRecord; +import teetime.common.record.IMonitoringRecord; +import teetime.common.record.misc.RegistryRecord; +import teetime.common.util.registry.ILookup; +import teetime.common.util.registry.Lookup; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IOutputPort; + +/** + * This is a reader which reads the records from a TCP port. + * + * @author Jan Waller, Nils Christian Ehmke + * + * @since 1.10 + */ +public class TCPReader extends AbstractFilter<TCPReader> { + + private static final int MESSAGE_BUFFER_SIZE = 65535; + + private final IOutputPort<TCPReader, IMonitoringRecord> outputPort = super.createOutputPort(); + + // BETTER use a non thread-safe implementation to increase performance. A thread-safe version is not necessary. + private final ILookup<String> stringRegistry = new Lookup<String>(); + private int port1 = 10133; + private int port2 = 10134; + + @Override + public void onPipelineStarts() throws Exception { + super.onPipelineStarts(); + + // FIXME use the implementation from the thread or from execute(), but not both + final TCPStringReader tcpStringReader = new TCPStringReader(this.port2, this.stringRegistry); + tcpStringReader.start(); + } + + @Override + public void onPipelineStops() { + super.logger.info("Shutdown of TCPReader requested."); + // TODO actually implement terminate! + super.onPipelineStops(); + } + + public final int getPort1() { + return this.port1; + } + + public final void setPort1(final int port1) { + this.port1 = port1; + } + + public final int getPort2() { + return this.port2; + } + + public final void setPort2(final int port2) { + this.port2 = port2; + } + + @Override + protected boolean execute(final Context<TCPReader> context) { + ServerSocketChannel serversocket = null; + try { + serversocket = ServerSocketChannel.open(); + serversocket.socket().bind(new InetSocketAddress(this.port1)); + if (super.logger.isDebugEnabled()) { + super.logger.debug("Listening on port " + this.port1); + } + // BEGIN also loop this one? + final SocketChannel socketChannel = serversocket.accept(); + final ByteBuffer buffer = ByteBuffer.allocateDirect(MESSAGE_BUFFER_SIZE); + while (socketChannel.read(buffer) != -1) { + buffer.flip(); + // System.out.println("Reading, remaining:" + buffer.remaining()); + try { + while (buffer.hasRemaining()) { + buffer.mark(); + final int clazzid = buffer.getInt(); + final long loggingTimestamp = buffer.getLong(); + final IMonitoringRecord record; + try { // NOCS (Nested try-catch) + record = AbstractMonitoringRecord.createFromByteBuffer(clazzid, buffer, this.stringRegistry); + record.setLoggingTimestamp(loggingTimestamp); + context.put(this.outputPort, record); + } catch (final MonitoringRecordException ex) { + super.logger.error("Failed to create record.", ex); + } + } + buffer.clear(); + } catch (final BufferUnderflowException ex) { + buffer.reset(); + // System.out.println("Underflow, remaining:" + buffer.remaining()); + buffer.compact(); + } + } + // System.out.println("Channel closing..."); + socketChannel.close(); + // END also loop this one? + } catch (final IOException ex) { + super.logger.error("Error while reading", ex); + return false; + } finally { + if (null != serversocket) { + try { + serversocket.close(); + } catch (final IOException e) { + if (super.logger.isDebugEnabled()) { + super.logger.debug("Failed to close TCP connection!", e); + } + } + } + } + return true; + } + +} + +/** + * + * @author Jan Waller + * + * @since 1.8 + */ +class TCPStringReader extends Thread { + + private static final int MESSAGE_BUFFER_SIZE = 65535; + + private static final Log LOG = LogFactory.getLog(TCPStringReader.class); + + private final int port; + private final ILookup<String> stringRegistry; + + public TCPStringReader(final int port, final ILookup<String> stringRegistry) { + this.port = port; + this.stringRegistry = stringRegistry; + } + + @Override + public void run() { + ServerSocketChannel serversocket = null; + try { + serversocket = ServerSocketChannel.open(); + serversocket.socket().bind(new InetSocketAddress(this.port)); + if (LOG.isDebugEnabled()) { + LOG.debug("Listening on port " + this.port); + } + // BEGIN also loop this one? + final SocketChannel socketChannel = serversocket.accept(); + final ByteBuffer buffer = ByteBuffer.allocateDirect(MESSAGE_BUFFER_SIZE); + while (socketChannel.read(buffer) != -1) { + buffer.flip(); + try { + while (buffer.hasRemaining()) { + buffer.mark(); + RegistryRecord.registerRecordInRegistry(buffer, this.stringRegistry); + } + buffer.clear(); + } catch (final BufferUnderflowException ex) { + buffer.reset(); + buffer.compact(); + } + } + socketChannel.close(); + // END also loop this one? + } catch (final IOException ex) { + LOG.error("Error while reading", ex); + } finally { + if (null != serversocket) { + try { + serversocket.close(); + } catch (final IOException e) { + if (LOG.isDebugEnabled()) { + LOG.debug("Failed to close TCP connection!", e); + } + } + } + } + } +} diff --git a/src/main/java/teetime/stage/kieker/File2RecordFilter.java b/src/main/java/teetime/stage/kieker/File2RecordFilter.java new file mode 100644 index 00000000..00af24c6 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/File2RecordFilter.java @@ -0,0 +1,144 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker; + +import java.io.File; + +import teetime.common.record.IMonitoringRecord; +import teetime.common.util.filesystem.BinaryCompressionMethod; +import teetime.common.util.filesystem.FSUtil; +import teetime.framework.concurrent.ConcurrentWorkStealingPipe; +import teetime.framework.concurrent.ConcurrentWorkStealingPipeFactory; +import teetime.framework.core.CompositeFilter; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.framework.sequential.MethodCallPipe; +import teetime.stage.FileExtensionFilter; +import teetime.stage.basic.merger.Merger; +import teetime.stage.kieker.className.ClassNameRegistryCreationFilter; +import teetime.stage.kieker.className.ClassNameRegistryRepository; +import teetime.stage.kieker.fileToRecord.BinaryFile2RecordFilter; +import teetime.stage.kieker.fileToRecord.DatFile2RecordFilter; +import teetime.stage.kieker.fileToRecord.ZipFile2RecordFilter; +import teetime.stage.predicate.IsDirectoryPredicate; +import teetime.stage.predicate.PredicateFilter; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class File2RecordFilter extends CompositeFilter { + + public final IInputPort<PredicateFilter<File>, File> fileInputPort; + + public final IOutputPort<Merger<IMonitoringRecord>, IMonitoringRecord> recordOutputPort; + + private ClassNameRegistryRepository classNameRegistryRepository; + + /** + * @since 1.10 + */ + public File2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) { + this.classNameRegistryRepository = classNameRegistryRepository; + + // FIXME does not yet work with more than one thread due to classNameRegistryRepository + // create stages + final PredicateFilter<File> isDirectoryFilter = new PredicateFilter<File>(new IsDirectoryPredicate()); + final ClassNameRegistryCreationFilter classNameRegistryCreationFilter = new ClassNameRegistryCreationFilter(this.classNameRegistryRepository); + final MonitoringLogDirectory2Files directory2FilesFilter = new MonitoringLogDirectory2Files(); + final FileExtensionFilter fileExtensionFilter = new FileExtensionFilter(); + final Merger<File> fileMerger = new Merger<File>(); + + final DatFile2RecordFilter datFile2RecordFilter = new DatFile2RecordFilter(this.classNameRegistryRepository); + final BinaryFile2RecordFilter binaryFile2RecordFilter = new BinaryFile2RecordFilter(this.classNameRegistryRepository); + final ZipFile2RecordFilter zipFile2RecordFilter = new ZipFile2RecordFilter(); + + final Merger<IMonitoringRecord> recordMerger = new Merger<IMonitoringRecord>(); + + // store ports due to readability reasons + final IOutputPort<FileExtensionFilter, File> normalFileOutputPort = fileExtensionFilter.createOutputPortForFileExtension(FSUtil.NORMAL_FILE_EXTENSION); + final IOutputPort<FileExtensionFilter, File> binFileOutputPort = fileExtensionFilter.createOutputPortForFileExtension(BinaryCompressionMethod.NONE + .getFileExtension()); + final IOutputPort<FileExtensionFilter, File> zipFileOutputPort = fileExtensionFilter.createOutputPortForFileExtension(FSUtil.ZIP_FILE_EXTENSION); + + // connect ports by pipes + MethodCallPipe.connect(isDirectoryFilter.matchingOutputPort, classNameRegistryCreationFilter.directoryInputPort); + MethodCallPipe.connect(isDirectoryFilter.mismatchingOutputPort, fileMerger.getNewInputPort()); // BETTER restructure pipeline + MethodCallPipe.connect(classNameRegistryCreationFilter.relayDirectoryOutputPort, directory2FilesFilter.directoryInputPort); + MethodCallPipe.connect(classNameRegistryCreationFilter.filePrefixOutputPort, directory2FilesFilter.filePrefixInputPort); + MethodCallPipe.connect(directory2FilesFilter.fileOutputPort, fileExtensionFilter.fileInputPort); + MethodCallPipe.connect(zipFileOutputPort, fileMerger.getNewInputPort()); + + final ConcurrentWorkStealingPipeFactory<File> concurrentWorkStealingPipeFactory0 = new ConcurrentWorkStealingPipeFactory<File>(); + final ConcurrentWorkStealingPipe<File> concurrentWorkStealingPipe0 = concurrentWorkStealingPipeFactory0.create(); + concurrentWorkStealingPipe0.setSourcePort(normalFileOutputPort); + concurrentWorkStealingPipe0.setTargetPort(datFile2RecordFilter.fileInputPort); + + final ConcurrentWorkStealingPipeFactory<File> concurrentWorkStealingPipeFactory1 = new ConcurrentWorkStealingPipeFactory<File>(); + final ConcurrentWorkStealingPipe<File> concurrentWorkStealingPipe1 = concurrentWorkStealingPipeFactory1.create(); + concurrentWorkStealingPipe1.setSourcePort(binFileOutputPort); + concurrentWorkStealingPipe1.setTargetPort(binaryFile2RecordFilter.fileInputPort); + + final ConcurrentWorkStealingPipeFactory<File> concurrentWorkStealingPipeFactory2 = new ConcurrentWorkStealingPipeFactory<File>(); + final ConcurrentWorkStealingPipe<File> concurrentWorkStealingPipe2 = concurrentWorkStealingPipeFactory2.create(); + concurrentWorkStealingPipe2.setSourcePort(fileMerger.outputPort); + concurrentWorkStealingPipe2.setTargetPort(zipFile2RecordFilter.fileInputPort); + + final ConcurrentWorkStealingPipeFactory<IMonitoringRecord> concurrentWorkStealingPipeFactoriesNormal = new ConcurrentWorkStealingPipeFactory<IMonitoringRecord>(); + final ConcurrentWorkStealingPipe<IMonitoringRecord> datPipe = concurrentWorkStealingPipeFactoriesNormal.create(); + datPipe.connect(datFile2RecordFilter.recordOutputPort, recordMerger.getNewInputPort()); + + final ConcurrentWorkStealingPipeFactory<IMonitoringRecord> concurrentWorkStealingPipeFactoriesBinary = new ConcurrentWorkStealingPipeFactory<IMonitoringRecord>(); + final ConcurrentWorkStealingPipe<IMonitoringRecord> binaryPipe = concurrentWorkStealingPipeFactoriesBinary.create(); + binaryPipe.connect(binaryFile2RecordFilter.recordOutputPort, recordMerger.getNewInputPort()); + + final ConcurrentWorkStealingPipeFactory<IMonitoringRecord> concurrentWorkStealingPipeFactoriesZip = new ConcurrentWorkStealingPipeFactory<IMonitoringRecord>(); + final ConcurrentWorkStealingPipe<IMonitoringRecord> zipPipe = concurrentWorkStealingPipeFactoriesZip.create(); + zipPipe.connect(zipFile2RecordFilter.recordOutputPort, recordMerger.getNewInputPort()); + + this.fileInputPort = isDirectoryFilter.inputPort; + this.recordOutputPort = recordMerger.outputPort; + + this.schedulableStages.add(isDirectoryFilter); + + // this.schedulableStages.add(classNameRegistryCreationFilter); + // this.schedulableStages.add(directory2FilesFilter); + // this.schedulableStages.add(fileMerger); + // this.schedulableStages.add(fileExtensionFilter); + + this.schedulableStages.add(datFile2RecordFilter); + this.schedulableStages.add(binaryFile2RecordFilter); + this.schedulableStages.add(zipFile2RecordFilter); + this.schedulableStages.add(recordMerger); + } + + /** + * @since 1.10 + */ + public File2RecordFilter() { + this(null); + } + + public ClassNameRegistryRepository getClassNameRegistryRepository() { + return this.classNameRegistryRepository; + } + + public void setClassNameRegistryRepository(final ClassNameRegistryRepository classNameRegistryRepository) { + this.classNameRegistryRepository = classNameRegistryRepository; + } + +} diff --git a/src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java b/src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java new file mode 100644 index 00000000..8d59f48a --- /dev/null +++ b/src/main/java/teetime/stage/kieker/MonitoringLogDirectory2Files.java @@ -0,0 +1,85 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker; + +import java.io.File; +import java.io.FileFilter; +import java.util.Comparator; + +import teetime.common.util.filesystem.BinaryCompressionMethod; +import teetime.common.util.filesystem.FSUtil; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.stage.io.Directory2FilesFilter; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class MonitoringLogDirectory2Files extends Directory2FilesFilter { + + public final IInputPort<Directory2FilesFilter, String> filePrefixInputPort = this.createInputPort(); + + /** + * @author Christian Wulf + * + * @since 1.10 + */ + static class MonitoringLogFileFilter implements FileFilter { + private String filePrefix; + + public boolean accept(final File pathname) { + final String name = pathname.getName(); + return pathname.isFile() + && name.startsWith(this.filePrefix) + && (name.endsWith(FSUtil.NORMAL_FILE_EXTENSION) || BinaryCompressionMethod.hasValidFileExtension(name)); + } + + public String getFilePrefix() { + return this.filePrefix; + } + + public void setFilePrefix(final String filePrefix) { + this.filePrefix = filePrefix; + } + } + + private static final Comparator<File> FILE_COMPARATOR = new Comparator<File>() { + public final int compare(final File f1, final File f2) { + return f1.compareTo(f2); // simplified (we expect no dirs!) + } + }; + + /** + * @since 1.10 + */ + public MonitoringLogDirectory2Files() { + super(new MonitoringLogFileFilter(), FILE_COMPARATOR); + } + + @Override + protected boolean execute(final Context<Directory2FilesFilter> context) { + final String filePrefix = context.tryTake(this.filePrefixInputPort); + if (filePrefix == null) { + return false; + } + + ((MonitoringLogFileFilter) this.getFilter()).setFilePrefix(filePrefix); + + return super.execute(context); + } +} diff --git a/src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java b/src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java new file mode 100644 index 00000000..15e300ed --- /dev/null +++ b/src/main/java/teetime/stage/kieker/className/ClassNameRegistry.java @@ -0,0 +1,29 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.className; + +import java.util.HashMap; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ClassNameRegistry extends HashMap<Integer, String> { + + private static final long serialVersionUID = -7254550212115937463L; + +} diff --git a/src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java new file mode 100644 index 00000000..85c350f0 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryCreationFilter.java @@ -0,0 +1,94 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.className; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; + +import kieker.analysis.stage.MappingFileParser; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ClassNameRegistryCreationFilter extends AbstractFilter<ClassNameRegistryCreationFilter> { + + public final IInputPort<ClassNameRegistryCreationFilter, File> directoryInputPort = this.createInputPort(); + + public final IOutputPort<ClassNameRegistryCreationFilter, File> relayDirectoryOutputPort = this.createOutputPort(); + public final IOutputPort<ClassNameRegistryCreationFilter, String> filePrefixOutputPort = this.createOutputPort(); + + private ClassNameRegistryRepository classNameRegistryRepository; + + private final MappingFileParser mappingFileParser; + + /** + * @since 1.10 + */ + public ClassNameRegistryCreationFilter(final ClassNameRegistryRepository classNameRegistryRepository) { + this(); + this.classNameRegistryRepository = classNameRegistryRepository; + } + + /** + * @since 1.10 + */ + public ClassNameRegistryCreationFilter() { + super(); + this.mappingFileParser = new MappingFileParser(this.logger); + } + + @Override + protected boolean execute(final Context<ClassNameRegistryCreationFilter> context) { + final File inputDir = context.tryTake(this.directoryInputPort); + if (inputDir == null) { + return false; + } + + final File mappingFile = this.mappingFileParser.findMappingFile(inputDir); + if (mappingFile == null) { + return true; + } + + try { + final ClassNameRegistry classNameRegistry = this.mappingFileParser.parseFromStream(new FileInputStream(mappingFile)); + this.classNameRegistryRepository.put(inputDir, classNameRegistry); + context.put(this.relayDirectoryOutputPort, inputDir); + + final String filePrefix = this.mappingFileParser.getFilePrefixFromMappingFile(mappingFile); + context.put(this.filePrefixOutputPort, filePrefix); + } catch (final FileNotFoundException e) { + this.logger.error("Mapping file not found.", e); // and skip this directory + } + + return true; + } + + public ClassNameRegistryRepository getClassNameRegistryRepository() { + return this.classNameRegistryRepository; + } + + public void setClassNameRegistryRepository(final ClassNameRegistryRepository classNameRegistryRepository) { + this.classNameRegistryRepository = classNameRegistryRepository; + } + +} diff --git a/src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java new file mode 100644 index 00000000..bbbd7911 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/className/ClassNameRegistryRepository.java @@ -0,0 +1,61 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.className; + +import java.io.File; +import java.util.concurrent.ConcurrentHashMap; + +/** + * This class represents a wrapper for a Map<String, ClassNameRegistry> ensuring that keys are + * <ul> + * <li>of the type <code>java.io.File</code> and + * <li>passed as absolute file paths. + * </ul> + * + * @author Christian Wulf + * + * @since 1.10 + */ +public class ClassNameRegistryRepository { + + private final ConcurrentHashMap<String, ClassNameRegistry> classNameRegistryRepository = new ConcurrentHashMap<String, ClassNameRegistry>(); + + /** + * @since 1.10 + */ + public ClassNameRegistry get(final File directory) { + return this.classNameRegistryRepository.get(directory.getAbsolutePath()); + } + + /** + * @since 1.10 + */ + public void put(final File directory, final ClassNameRegistry classNameRegistry) { + this.classNameRegistryRepository.put(directory.getAbsolutePath(), classNameRegistry); + } + + /** + * @since 1.10 + */ + public int size() { + return this.classNameRegistryRepository.size(); + } + + @Override + public String toString() { + return this.classNameRegistryRepository.toString(); + } +} diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java new file mode 100644 index 00000000..175efeb7 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/fileToRecord/BinaryFile2RecordFilter.java @@ -0,0 +1,113 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.fileToRecord; + +import java.io.DataInputStream; +import java.io.File; +import java.io.IOException; + +import kieker.analysis.stage.RecordFromBinaryFileCreator; +import teetime.common.exception.MonitoringRecordException; +import teetime.common.record.IMonitoringRecord; +import teetime.common.util.filesystem.BinaryCompressionMethod; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.stage.kieker.className.ClassNameRegistryRepository; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class BinaryFile2RecordFilter extends AbstractFilter<BinaryFile2RecordFilter> { + + private static final int MB = 1024 * 1024; + + public final IInputPort<BinaryFile2RecordFilter, File> fileInputPort = this.createInputPort(); + public final IOutputPort<BinaryFile2RecordFilter, IMonitoringRecord> recordOutputPort = this.createOutputPort(); + + private RecordFromBinaryFileCreator recordFromBinaryFileCreator; + + private ClassNameRegistryRepository classNameRegistryRepository; + + /** + * @since 1.10 + */ + public BinaryFile2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) { + this(); + this.classNameRegistryRepository = classNameRegistryRepository; + } + + /** + * @since 1.10 + */ + public BinaryFile2RecordFilter() { + super(); + } + + @Override + public void onPipelineStarts() throws Exception { + this.recordFromBinaryFileCreator = new RecordFromBinaryFileCreator(this.logger, this.classNameRegistryRepository); + super.onPipelineStarts(); + } + + @Override + protected boolean execute(final Context<BinaryFile2RecordFilter> context) { + final File binaryFile = context.tryTake(this.fileInputPort); + if (binaryFile == null) { + return false; + } + + try { + final BinaryCompressionMethod method = BinaryCompressionMethod.getByFileExtension(binaryFile.getName()); + final DataInputStream in = method.getDataInputStream(binaryFile, 1 * MB); + try { + IMonitoringRecord record = this.recordFromBinaryFileCreator.createRecordFromBinaryFile(binaryFile, in); + while (record != null) { + context.put(this.recordOutputPort, record); + record = this.recordFromBinaryFileCreator.createRecordFromBinaryFile(binaryFile, in); + } + } catch (final MonitoringRecordException e) { + this.logger.error("Error reading file: " + binaryFile, e); + } finally { + if (in != null) { + try { + in.close(); + } catch (final IOException ex) { + this.logger.error("Exception while closing input stream for processing input file", ex); + } + } + } + } catch (final IOException e) { + this.logger.error("Error reading file: " + binaryFile, e); + } catch (final IllegalArgumentException e) { + this.logger.warn("Unknown file extension for file: " + binaryFile); + } + + return true; + } + + public ClassNameRegistryRepository getClassNameRegistryRepository() { + return this.classNameRegistryRepository; + } + + public void setClassNameRegistryRepository(final ClassNameRegistryRepository classNameRegistryRepository) { + this.classNameRegistryRepository = classNameRegistryRepository; + } + +} diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java new file mode 100644 index 00000000..ea9c0fd3 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/fileToRecord/DatFile2RecordFilter.java @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.fileToRecord; + +import java.io.File; + +import teetime.common.record.IMonitoringRecord; +import teetime.framework.concurrent.ConcurrentWorkStealingPipe; +import teetime.framework.concurrent.ConcurrentWorkStealingPipeFactory; +import teetime.framework.core.CompositeFilter; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.stage.io.File2TextLinesFilter; +import teetime.stage.kieker.className.ClassNameRegistryRepository; +import teetime.stage.kieker.fileToRecord.textLine.TextLine2RecordFilter; +import teetime.stage.util.TextLine; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class DatFile2RecordFilter extends CompositeFilter { + + public final IInputPort<File2TextLinesFilter, File> fileInputPort; + + public final IOutputPort<TextLine2RecordFilter, IMonitoringRecord> recordOutputPort; + + public DatFile2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) { + final File2TextLinesFilter file2TextLinesFilter = new File2TextLinesFilter(); + final TextLine2RecordFilter textLine2RecordFilter = new TextLine2RecordFilter(classNameRegistryRepository); + + // FIXME extract pipe implementation + final ConcurrentWorkStealingPipeFactory<TextLine> concurrentWorkStealingPipeFactory = new ConcurrentWorkStealingPipeFactory<TextLine>(); + final ConcurrentWorkStealingPipe<TextLine> pipe = concurrentWorkStealingPipeFactory.create(); + pipe.connect(file2TextLinesFilter.textLineOutputPort, textLine2RecordFilter.textLineInputPort); + + this.fileInputPort = file2TextLinesFilter.fileInputPort; + this.recordOutputPort = textLine2RecordFilter.recordOutputPort; + + this.schedulableStages.add(file2TextLinesFilter); + this.schedulableStages.add(textLine2RecordFilter); + } +} diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java new file mode 100644 index 00000000..12d274b8 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/fileToRecord/ZipFile2RecordFilter.java @@ -0,0 +1,134 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.fileToRecord; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.DataInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; + +import kieker.analysis.stage.MappingFileParser; +import teetime.common.record.IMonitoringRecord; +import teetime.common.util.filesystem.FSUtil; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.stage.kieker.className.ClassNameRegistry; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ZipFile2RecordFilter extends AbstractFilter<ZipFile2RecordFilter> { + + public final IInputPort<ZipFile2RecordFilter, File> fileInputPort = this.createInputPort(); + + public final IOutputPort<ZipFile2RecordFilter, IMonitoringRecord> recordOutputPort = this.createOutputPort(); + + private final MappingFileParser mappingFileParser; + + /** + * @since 1.10 + */ + public ZipFile2RecordFilter() { + this.mappingFileParser = new MappingFileParser(this.logger); + } + + @Override + protected boolean execute(final Context<ZipFile2RecordFilter> context) { + final File zipFile = context.tryTake(this.fileInputPort); + if (zipFile == null) { + return false; + } + + final InputStream mappingFileInputStream = this.findMappingFileInputStream(zipFile); + if (mappingFileInputStream == null) { + return true; + } + final ClassNameRegistry classNameRegistry = this.mappingFileParser.parseFromStream(mappingFileInputStream); + + try { + this.createAndSendRecordsFromZipFile(context, zipFile, classNameRegistry); + } catch (final FileNotFoundException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + return true; + } + + private void createAndSendRecordsFromZipFile(final Context<ZipFile2RecordFilter> context, final File zipFile, final ClassNameRegistry classNameRegistry) + throws FileNotFoundException { + final ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile)); + final BufferedReader reader; + try { + reader = new BufferedReader(new InputStreamReader(zipInputStream, FSUtil.ENCODING)); + } catch (final UnsupportedEncodingException e) { + this.logger.error("This exception should never occur.", e); + return; + } + final DataInputStream input = new DataInputStream(new BufferedInputStream(zipInputStream, 1024 * 1024)); + + ZipEntry zipEntry; + try { + while (null != (zipEntry = zipInputStream.getNextEntry())) { // NOCS NOPMD + final String filename = zipEntry.getName(); + // TODO + } + } catch (final IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + private InputStream findMappingFileInputStream(final File zipFile) { + ZipInputStream zipInputStream = null; + try { + zipInputStream = new ZipInputStream(new FileInputStream(zipFile)); + ZipEntry zipEntry; + while ((null != (zipEntry = zipInputStream.getNextEntry())) && !zipEntry.getName().equals(FSUtil.MAP_FILENAME)) { // NOCS NOPMD + // do nothing, just skip to the map file if present + } + if (null == zipEntry) { + this.logger.error("The zip file does not contain a Kieker log: " + zipFile.toString()); + return null; + } + return zipInputStream; + } catch (final IOException ex) { + this.logger.error("Error accessing ZipInputStream", ex); + } finally { + if (null != zipInputStream) { + try { + zipInputStream.close(); + } catch (final IOException ex) { + this.logger.error("Failed to close ZipInputStream", ex); + } + } + } + + return null; + } +} diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java new file mode 100644 index 00000000..dbaa1f06 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2MappingRegistryFilter.java @@ -0,0 +1,76 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.kieker.fileToRecord.textLine; + +import java.util.Map; + +import teetime.common.util.filesystem.FSUtil; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TextLine2MappingRegistryFilter extends AbstractFilter<TextLine2MappingRegistryFilter> { + + public final IInputPort<TextLine2MappingRegistryFilter, String> TEXT_LINE = this.createInputPort(); + + private final Map<Integer, String> stringRegistry; + + public TextLine2MappingRegistryFilter(final Map<Integer, String> stringRegistry) { + this.stringRegistry = stringRegistry; + } + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<TextLine2MappingRegistryFilter> context) { + final String textLine = context.tryTake(this.TEXT_LINE); + if (textLine == null) { + return false; + } + + final int split = textLine.indexOf('='); + if (split == -1) { + this.logger.error("Failed to find character '=' in line: {" + textLine + "}. It must consist of a ID=VALUE pair."); + return true; + } + final String key = textLine.substring(0, split); + // BETTER execute split instead of checking it before with multiple string operations + final String value = FSUtil.decodeNewline(textLine.substring(split + 1)); + // the leading $ is optional + final Integer id; + try { + id = Integer.valueOf((key.charAt(0) == '$') ? key.substring(1) : key); // NOCS + } catch (final NumberFormatException ex) { + this.logger.error("Error reading mapping file, id must be integer", ex); + return true; // continue on errors + } + final String prevVal = this.stringRegistry.put(id, value); + if (prevVal != null) { + this.logger.error("Found additional entry for id='" + id + "', old value was '" + prevVal + "' new value is '" + value + "'"); + return true; + } + + return true; + } + +} diff --git a/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java new file mode 100644 index 00000000..9960a1fe --- /dev/null +++ b/src/main/java/teetime/stage/kieker/fileToRecord/textLine/TextLine2RecordFilter.java @@ -0,0 +1,117 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.kieker.fileToRecord.textLine; + +import java.util.HashSet; +import java.util.Set; + +import kieker.analysis.stage.RecordFromTextLineCreator; +import teetime.common.exception.IllegalRecordFormatException; +import teetime.common.exception.MonitoringRecordException; +import teetime.common.exception.UnknownRecordTypeException; +import teetime.common.record.IMonitoringRecord; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.stage.MappingException; +import teetime.stage.kieker.className.ClassNameRegistryRepository; +import teetime.stage.util.TextLine; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TextLine2RecordFilter extends AbstractFilter<TextLine2RecordFilter> { + + public final IInputPort<TextLine2RecordFilter, TextLine> textLineInputPort = this.createInputPort(); + + public final IOutputPort<TextLine2RecordFilter, IMonitoringRecord> recordOutputPort = this.createOutputPort(); + + private final Set<String> unknownTypesObserved = new HashSet<String>(); + + private boolean ignoreUnknownRecordTypes; + + private boolean abortDueToUnknownRecordType; + + private RecordFromTextLineCreator recordFromTextLineCreator; + + /** + * @since 1.10 + */ + public TextLine2RecordFilter(final ClassNameRegistryRepository classNameRegistryRepository) { + this.recordFromTextLineCreator = new RecordFromTextLineCreator(classNameRegistryRepository); + } + + /** + * @since 1.10 + */ + public TextLine2RecordFilter() { + super(); + } + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<TextLine2RecordFilter> context) { + final TextLine textLine = context.tryTake(this.textLineInputPort); + if (textLine == null) { + return false; + } + + try { + final IMonitoringRecord record = this.recordFromTextLineCreator.createRecordFromLine(textLine.getTextFile(), textLine.getTextLine()); + context.put(this.recordOutputPort, record); + } catch (final MonitoringRecordException e) { + this.logger.error("Could not create record from text line: '" + textLine + "'", e); + } catch (final IllegalRecordFormatException e) { + this.logger.error("Illegal record format: " + textLine, e); + } catch (final MappingException e) { + this.logger.error("", e); + } catch (final UnknownRecordTypeException e) { + final String classname = e.getClassName(); + if (!this.ignoreUnknownRecordTypes) { + this.abortDueToUnknownRecordType = true; + this.logger.error("Failed to load record type " + classname, e); + } else if (!this.unknownTypesObserved.contains(classname)) { + this.unknownTypesObserved.add(classname); + this.logger.error("Failed to load record type " + classname, e); // log once for this type + } + } + + return true; + } + + public boolean isIgnoreUnknownRecordTypes() { + return this.ignoreUnknownRecordTypes; + } + + public void setIgnoreUnknownRecordTypes(final boolean ignoreUnknownRecordTypes) { + this.ignoreUnknownRecordTypes = ignoreUnknownRecordTypes; + } + + public RecordFromTextLineCreator getRecordFromTextLineCreator() { + return this.recordFromTextLineCreator; + } + + public void setRecordFromTextLineCreator(final RecordFromTextLineCreator recordFromTextLineCreator) { + this.recordFromTextLineCreator = recordFromTextLineCreator; + } + +} diff --git a/src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java b/src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java new file mode 100644 index 00000000..360258d8 --- /dev/null +++ b/src/main/java/teetime/stage/kieker/traceReconstruction/TraceReconstructionFilter.java @@ -0,0 +1,191 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.kieker.traceReconstruction; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import teetime.analysis.plugin.filter.flow.TraceEventRecords; +import teetime.analysis.plugin.filter.flow.reconstruction.TraceBuffer; +import teetime.common.record.flow.IFlowRecord; +import teetime.common.record.flow.trace.AbstractTraceEvent; +import teetime.common.record.flow.trace.TraceMetadata; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.util.concurrent.hashmap.ConcurrentHashMapWithDefault; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TraceReconstructionFilter extends AbstractFilter<TraceReconstructionFilter> { + + public final IInputPort<TraceReconstructionFilter, Long> timestampInputPort = this.createInputPort(); + public final IInputPort<TraceReconstructionFilter, IFlowRecord> recordInputPort = this.createInputPort(); + + public final IOutputPort<TraceReconstructionFilter, TraceEventRecords> traceInvalidOutputPort = this.createOutputPort(); + public final IOutputPort<TraceReconstructionFilter, TraceEventRecords> traceValidOutputPort = this.createOutputPort(); + + private TimeUnit timeunit; + private long maxTraceDuration; + private long maxTraceTimeout; + private boolean timeout; + private long maxEncounteredLoggingTimestamp = -1; + + private Map<Long, TraceBuffer> traceId2trace = new ConcurrentHashMapWithDefault<Long, TraceBuffer>(new TraceBuffer()); + + @Override + protected boolean execute(final Context<TraceReconstructionFilter> context) { + final Long timestamp = context.tryTake(this.timestampInputPort); + if (timestamp != null) { + if (this.timeout) { + this.processTimeoutQueue(timestamp, context); + } + return true; + } + + final IFlowRecord record = context.tryTake(this.recordInputPort); + if (record != null) { + final Long traceId = this.reconstructTrace(record); + if (traceId != null) { + this.putIfFinished(traceId, context); + this.processTimestamp(record, context); + } + return true; + } + + return false; + } + + private void processTimestamp(final IFlowRecord record, final Context<TraceReconstructionFilter> context) { + if (this.timeout) { + synchronized (this) { + final long loggingTimestamp = this.getTimestamp(record); + // can we assume a rough order of logging timestamps? (yes, except with DB reader) + if (loggingTimestamp > this.maxEncounteredLoggingTimestamp) { + this.maxEncounteredLoggingTimestamp = loggingTimestamp; + } + this.processTimeoutQueue(this.maxEncounteredLoggingTimestamp, context); + } + } + } + + private long getTimestamp(final IFlowRecord record) { + if (record instanceof AbstractTraceEvent) { + return ((AbstractTraceEvent) record).getTimestamp(); + } + return -1; + } + + private void putIfFinished(final Long traceId, final Context<TraceReconstructionFilter> context) { + final TraceBuffer traceBuffer = this.traceId2trace.get(traceId); + if (traceBuffer.isFinished()) { + synchronized (this) { // has to be synchronized because of timeout cleanup + this.traceId2trace.remove(traceId); + } + this.put(traceBuffer, context); + } + } + + private Long reconstructTrace(final IFlowRecord record) { + Long traceId = null; + if (record instanceof TraceMetadata) { + traceId = ((TraceMetadata) record).getTraceId(); + final TraceBuffer traceBuffer = this.traceId2trace.get(traceId); + + traceBuffer.setTrace((TraceMetadata) record); + } else if (record instanceof AbstractTraceEvent) { + traceId = ((AbstractTraceEvent) record).getTraceId(); + final TraceBuffer traceBuffer = this.traceId2trace.get(traceId); + + traceBuffer.insertEvent((AbstractTraceEvent) record); + } + + return traceId; + } + + @Override + public void onPipelineStarts() throws Exception { + this.timeout = !((this.maxTraceTimeout == Long.MAX_VALUE) && (this.maxTraceDuration == Long.MAX_VALUE)); + super.onPipelineStarts(); + } + + private void processTimeoutQueue(final long timestamp, final Context<TraceReconstructionFilter> context) { + final long duration = timestamp - this.maxTraceDuration; + final long traceTimeout = timestamp - this.maxTraceTimeout; + + for (final Iterator<Entry<Long, TraceBuffer>> iterator = this.traceId2trace.entrySet().iterator(); iterator.hasNext();) { + final TraceBuffer traceBuffer = iterator.next().getValue(); + if ((traceBuffer.getMaxLoggingTimestamp() <= traceTimeout) // long time no see + || (traceBuffer.getMinLoggingTimestamp() <= duration)) { // max duration is gone + this.put(traceBuffer, context); + iterator.remove(); + } + } + } + + private void put(final TraceBuffer traceBuffer, final Context<TraceReconstructionFilter> context) { + final IOutputPort<TraceReconstructionFilter, TraceEventRecords> outputPort = + (traceBuffer.isInvalid()) ? this.traceInvalidOutputPort : this.traceValidOutputPort; + context.put(outputPort, traceBuffer.toTraceEvents()); + } + + public TimeUnit getTimeunit() { + return this.timeunit; + } + + public void setTimeunit(final TimeUnit timeunit) { + this.timeunit = timeunit; + } + + public long getMaxTraceDuration() { + return this.maxTraceDuration; + } + + public void setMaxTraceDuration(final long maxTraceDuration) { + this.maxTraceDuration = maxTraceDuration; + } + + public long getMaxTraceTimeout() { + return this.maxTraceTimeout; + } + + public void setMaxTraceTimeout(final long maxTraceTimeout) { + this.maxTraceTimeout = maxTraceTimeout; + } + + public long getMaxEncounteredLoggingTimestamp() { + return this.maxEncounteredLoggingTimestamp; + } + + public void setMaxEncounteredLoggingTimestamp(final long maxEncounteredLoggingTimestamp) { + this.maxEncounteredLoggingTimestamp = maxEncounteredLoggingTimestamp; + } + + public Map<Long, TraceBuffer> getTraceId2trace() { + return this.traceId2trace; + } + + public void setTraceId2trace(final Map<Long, TraceBuffer> traceId2trace) { + this.traceId2trace = traceId2trace; + } + +} diff --git a/src/main/java/teetime/stage/predicate/FileExtensionPredicate.java b/src/main/java/teetime/stage/predicate/FileExtensionPredicate.java new file mode 100644 index 00000000..c7f3bb87 --- /dev/null +++ b/src/main/java/teetime/stage/predicate/FileExtensionPredicate.java @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import java.io.File; + +import com.google.common.base.Predicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class FileExtensionPredicate implements Predicate<File> { + + private final String fileExtension; + + public FileExtensionPredicate(final String fileExtension) { + this.fileExtension = fileExtension; + } + + public boolean apply(final File file) { + return file.getName().endsWith(this.fileExtension); + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java b/src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java new file mode 100644 index 00000000..21b09576 --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsDirectoryPredicate.java @@ -0,0 +1,33 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import java.io.File; + +import com.google.common.base.Predicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IsDirectoryPredicate implements Predicate<File> { + + public boolean apply(final File file) { + return file.isDirectory(); + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java b/src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java new file mode 100644 index 00000000..10adfe8e --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsIMonitoringRecordInRange.java @@ -0,0 +1,39 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import teetime.common.record.IMonitoringRecord; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IsIMonitoringRecordInRange extends IsTimestampInRange<IMonitoringRecord> { + + /** + * @since 1.10 + */ + public IsIMonitoringRecordInRange(final long min, final long max) { + super(min, max); + } + + public boolean apply(final IMonitoringRecord record) { + final long timestamp = record.getLoggingTimestamp(); + return this.isInRange(timestamp); + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java b/src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java new file mode 100644 index 00000000..3e0528ee --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsInstanceOfPredicate.java @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import java.util.Collection; + +import com.google.common.base.Predicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IsInstanceOfPredicate<T> implements Predicate<T> { + + private final Collection<Class<?>> acceptedClasses; + + /** + * @since 1.10 + */ + public IsInstanceOfPredicate(final Collection<Class<?>> acceptedClasses) { + this.acceptedClasses = acceptedClasses; + } + + public boolean apply(final T object) { + for (final Class<?> acceptedClazz : this.acceptedClasses) { + if (acceptedClazz.isInstance(object)) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java new file mode 100644 index 00000000..d660cbcf --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordInRange.java @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import teetime.common.record.controlflow.OperationExecutionRecord; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IsOperationExecutionRecordInRange extends IsTimestampInRange<OperationExecutionRecord> { + + /** + * @since 1.10 + */ + public IsOperationExecutionRecordInRange(final long min, final long max) { + super(min, max); + } + + public boolean apply(final OperationExecutionRecord record) { + final long tinTimestamp = record.getTin(); + final boolean isTinInRange = this.isInRange(tinTimestamp); + final long toutTimestamp = record.getTout(); + final boolean isToutInRange = this.isInRange(toutTimestamp); + return isTinInRange && isToutInRange; + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java new file mode 100644 index 00000000..fbd2bc6b --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsOperationExecutionRecordTraceIdPredicate.java @@ -0,0 +1,41 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import java.util.Set; + +import teetime.common.record.controlflow.OperationExecutionRecord; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IsOperationExecutionRecordTraceIdPredicate extends IsTraceIdPredicate<OperationExecutionRecord> { + + /** + * @since 1.10 + */ + public IsOperationExecutionRecordTraceIdPredicate(final boolean acceptAllTraces, final Set<Long> selectedTraceIds) { + super(acceptAllTraces, selectedTraceIds); + } + + public boolean apply(final OperationExecutionRecord record) { + final Long traceId = record.getTraceId(); + return this.acceptId(traceId); + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java b/src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java new file mode 100644 index 00000000..2c29775a --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsSuperTypePredicate.java @@ -0,0 +1,47 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import java.util.Collection; + +import com.google.common.base.Predicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IsSuperTypePredicate<T> implements Predicate<T> { + + private final Collection<Class<?>> acceptedClasses; + + /** + * @since 1.10 + */ + public IsSuperTypePredicate(final Collection<Class<?>> acceptedClasses) { + this.acceptedClasses = acceptedClasses; + } + + public boolean apply(final T object) { + for (final Class<?> acceptedClazz : this.acceptedClasses) { + if (acceptedClazz.isAssignableFrom(object.getClass())) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsTimestampInRange.java b/src/main/java/teetime/stage/predicate/IsTimestampInRange.java new file mode 100644 index 00000000..42ad92d5 --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsTimestampInRange.java @@ -0,0 +1,42 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import com.google.common.base.Predicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public abstract class IsTimestampInRange<T> implements Predicate<T> { + + private final long min; + private final long max; + + /** + * @since 1.10 + */ + public IsTimestampInRange(final long min, final long max) { + this.min = min; + this.max = max; + } + + protected boolean isInRange(final long timestamp) { + return (timestamp >= this.min) && (timestamp <= this.max); + } + +} diff --git a/src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java b/src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java new file mode 100644 index 00000000..e1d369b3 --- /dev/null +++ b/src/main/java/teetime/stage/predicate/IsTraceIdPredicate.java @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import java.util.Set; + +import com.google.common.base.Predicate; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public abstract class IsTraceIdPredicate<T> implements Predicate<T> { + + private final boolean acceptAllTraces; + private final Set<Long> selectedTraceIds; + + /** + * @since 1.10 + */ + public IsTraceIdPredicate(final boolean acceptAllTraces, final Set<Long> selectedTraceIds) { + this.acceptAllTraces = acceptAllTraces; + this.selectedTraceIds = selectedTraceIds; + } + + protected final boolean acceptId(final Long traceId) { + return (this.acceptAllTraces || this.selectedTraceIds.contains(traceId)); + } + +} diff --git a/src/main/java/teetime/stage/predicate/PredicateFilter.java b/src/main/java/teetime/stage/predicate/PredicateFilter.java new file mode 100644 index 00000000..9a672bfa --- /dev/null +++ b/src/main/java/teetime/stage/predicate/PredicateFilter.java @@ -0,0 +1,72 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.predicate; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +import com.google.common.base.Predicate; + +/** + * @author Nils Christian Ehmke + * @param <T> + * + * @since 1.10 + */ +public class PredicateFilter<T> extends AbstractFilter<PredicateFilter<T>> { + + public final IInputPort<PredicateFilter<T>, T> inputPort = this.createInputPort(); + + public final IOutputPort<PredicateFilter<T>, T> matchingOutputPort = this.createOutputPort(); + public final IOutputPort<PredicateFilter<T>, T> mismatchingOutputPort = this.createOutputPort(); + + private Predicate<T> predicate; + + public PredicateFilter(final Predicate<T> predicate) { + this.setPredicate(predicate); + } + + public PredicateFilter() { + super(); + } + + public Predicate<T> getPredicate() { + return this.predicate; + } + + public void setPredicate(final Predicate<T> predicate) { + this.predicate = predicate; + } + + @Override + protected boolean execute(final Context<PredicateFilter<T>> context) { + final T inputObject = context.tryTake(this.inputPort); + if (inputObject == null) { + return false; + } + + if (this.predicate.apply(inputObject)) { + context.put(this.matchingOutputPort, inputObject); + } else { + context.put(this.mismatchingOutputPort, inputObject); + } + + return true; + } + +} diff --git a/src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java b/src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java new file mode 100644 index 00000000..81856327 --- /dev/null +++ b/src/main/java/teetime/stage/stringBuffer/StringBufferFilter.java @@ -0,0 +1,93 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.stringBuffer; + +import java.util.ArrayList; +import java.util.Collection; + +import teetime.analysis.plugin.filter.forward.util.KiekerHashMap; +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; +import teetime.stage.stringBuffer.handler.AbstractDataTypeHandler; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StringBufferFilter<T> extends AbstractFilter<StringBufferFilter<T>> { + + public final IInputPort<StringBufferFilter<T>, T> objectInputPort = this.createInputPort(); + + public final IOutputPort<StringBufferFilter<T>, T> objectOutputPort = this.createOutputPort(); + + // BETTER use a non shared data structure to avoid synchronization between threads + private KiekerHashMap kiekerHashMap = new KiekerHashMap(); + + private Collection<AbstractDataTypeHandler<?>> dataTypeHandlers = new ArrayList<AbstractDataTypeHandler<?>>(); + + @Override + protected boolean execute(final Context<StringBufferFilter<T>> context) { + final T object = context.tryTake(this.objectInputPort); + if (object == null) { + return false; + } + + final T returnedObject = this.handle(object); + context.put(this.objectOutputPort, returnedObject); + + return true; + } + + @Override + public void onPipelineStarts() throws Exception { + for (final AbstractDataTypeHandler<?> handler : this.dataTypeHandlers) { + handler.setLogger(this.logger); + handler.setStringRepository(this.kiekerHashMap); + } + super.onPipelineStarts(); + } + + private T handle(final T object) { + for (final AbstractDataTypeHandler<?> handler : this.dataTypeHandlers) { + if (handler.canHandle(object)) { + @SuppressWarnings("unchecked") + final T returnedObject = ((AbstractDataTypeHandler<T>) handler).handle(object); + return returnedObject; + } + } + return object; // else relay given object + } + + public KiekerHashMap getKiekerHashMap() { + return this.kiekerHashMap; + } + + public void setKiekerHashMap(final KiekerHashMap kiekerHashMap) { + this.kiekerHashMap = kiekerHashMap; + } + + public Collection<AbstractDataTypeHandler<?>> getDataTypeHandlers() { + return this.dataTypeHandlers; + } + + public void setDataTypeHandlers(final Collection<AbstractDataTypeHandler<?>> dataTypeHandlers) { + this.dataTypeHandlers = dataTypeHandlers; + } + +} diff --git a/src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java b/src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java new file mode 100644 index 00000000..3752aa5e --- /dev/null +++ b/src/main/java/teetime/stage/stringBuffer/handler/AbstractDataTypeHandler.java @@ -0,0 +1,55 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.stringBuffer.handler; + +import teetime.analysis.plugin.filter.forward.util.KiekerHashMap; +import teetime.common.logging.Log; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public abstract class AbstractDataTypeHandler<T> { + + protected Log logger; + protected KiekerHashMap stringRepository; + + /** + * @since 1.10 + */ + public abstract boolean canHandle(Object object); + + /** + * @since 1.10 + */ + public abstract T handle(T object); + + /** + * @since 1.10 + */ + public void setLogger(final Log logger) { + this.logger = logger; + } + + /** + * @since 1.10 + */ + public void setStringRepository(final KiekerHashMap stringRepository) { + this.stringRepository = stringRepository; + } + +} diff --git a/src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java b/src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java new file mode 100644 index 00000000..98de7481 --- /dev/null +++ b/src/main/java/teetime/stage/stringBuffer/handler/IMonitoringRecordHandler.java @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.stringBuffer.handler; + +import teetime.common.exception.MonitoringRecordException; +import teetime.common.record.AbstractMonitoringRecord; +import teetime.common.record.IMonitoringRecord; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class IMonitoringRecordHandler extends AbstractDataTypeHandler<IMonitoringRecord> { + + @Override + public boolean canHandle(final Object object) { + return object instanceof IMonitoringRecord; + } + + @Override + public IMonitoringRecord handle(final IMonitoringRecord monitoringRecord) { + final Object[] objects = monitoringRecord.toArray(); + + boolean stringBuffered = false; + for (int i = 0; i < objects.length; i++) { + if (objects[i] instanceof String) { + objects[i] = this.stringRepository.get((String) objects[i]); + stringBuffered = true; + } + } + + if (stringBuffered) { + try { + final IMonitoringRecord newRecord = AbstractMonitoringRecord.createFromArray(monitoringRecord.getClass(), objects); + newRecord.setLoggingTimestamp(monitoringRecord.getLoggingTimestamp()); + return newRecord; + } catch (final MonitoringRecordException ex) { + this.logger.warn("Failed to recreate buffered monitoring record: " + monitoringRecord.toString(), ex); + } + } + + return monitoringRecord; + } + +} diff --git a/src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java b/src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java new file mode 100644 index 00000000..82c356fa --- /dev/null +++ b/src/main/java/teetime/stage/stringBuffer/handler/StringHandler.java @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.stringBuffer.handler; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StringHandler extends AbstractDataTypeHandler<String> { + + @Override + public boolean canHandle(final Object object) { + return object instanceof String; + } + + @Override + public String handle(final String object) { + return this.stringRepository.get(object); + } + +} diff --git a/src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java b/src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java new file mode 100644 index 00000000..665fcf4e --- /dev/null +++ b/src/main/java/teetime/stage/throughput/AnalysisThroughputFilter.java @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.throughput; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; +import teetime.framework.core.IOutputPort; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class AnalysisThroughputFilter<T> extends AbstractFilter<AnalysisThroughputFilter<T>> { + + public final IInputPort<AnalysisThroughputFilter<T>, T> objectInputPort = this.createInputPort(); + public final IInputPort<AnalysisThroughputFilter<T>, Long> timestampInputPort = this.createInputPort(); + + public final IOutputPort<AnalysisThroughputFilter<T>, T> objectOutputPort = this.createOutputPort(); + public final IOutputPort<AnalysisThroughputFilter<T>, ThroughputAnalysisResult> analysisOutputPort = this.createOutputPort(); + + private long lastTimestampInNs; + private int numObjectsPassed; + + @Override + protected boolean execute(final Context<AnalysisThroughputFilter<T>> context) { + final Long timestampInNs = context.tryTake(this.timestampInputPort); + if (timestampInNs == null) { + final T object = context.tryTake(this.objectInputPort); + context.put(this.objectOutputPort, object); + this.numObjectsPassed++; + } else { + final long durationInNs = timestampInNs - this.lastTimestampInNs; + context.put(this.analysisOutputPort, new ThroughputAnalysisResult(durationInNs, this.numObjectsPassed)); + this.reset(timestampInNs); + } + + return true; + } + + private void reset(final Long timestampInNs) { + this.numObjectsPassed = 0; + this.lastTimestampInNs = timestampInNs; + } + +} diff --git a/src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java b/src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java new file mode 100644 index 00000000..35c686b0 --- /dev/null +++ b/src/main/java/teetime/stage/throughput/ThroughputAnalysisResult.java @@ -0,0 +1,44 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.throughput; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ThroughputAnalysisResult { + + private final long durationInNs; + private final int numObjectsPassed; + + /** + * @since 1.10 + */ + public ThroughputAnalysisResult(final long durationInNs, final int numObjectsPassed) { + this.durationInNs = durationInNs; + this.numObjectsPassed = numObjectsPassed; + } + + public long getDurationInNs() { + return durationInNs; + } + + public int getNumObjectsPassed() { + return numObjectsPassed; + } + +} diff --git a/src/main/java/teetime/stage/util/TextLine.java b/src/main/java/teetime/stage/util/TextLine.java new file mode 100644 index 00000000..cc2d9acd --- /dev/null +++ b/src/main/java/teetime/stage/util/TextLine.java @@ -0,0 +1,45 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.stage.util; + +import java.io.File; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class TextLine { + + private final File textFile; + private final String textLine; + + /** + * @since 1.10 + */ + public TextLine(final File textFile, final String textLine) { + this.textFile = textFile; + this.textLine = textLine; + } + + public File getTextFile() { + return this.textFile; + } + + public String getTextLine() { + return this.textLine; + } +} diff --git a/src/main/java/teetime/stage/visualization/IWebVisualizationSink.java b/src/main/java/teetime/stage/visualization/IWebVisualizationSink.java new file mode 100644 index 00000000..b352004a --- /dev/null +++ b/src/main/java/teetime/stage/visualization/IWebVisualizationSink.java @@ -0,0 +1,35 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.visualization; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + * + * @param <I> + * The type of the input ports + */ +public interface IWebVisualizationSink { + + public String getHeader(); + + public String getInitialContent(); + + public String getUpdatedContent(); + +} diff --git a/src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java b/src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java new file mode 100644 index 00000000..2bf71599 --- /dev/null +++ b/src/main/java/teetime/stage/visualization/PlainTextWebVisualizationSink.java @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.stage.visualization; + +import teetime.framework.core.AbstractFilter; +import teetime.framework.core.Context; +import teetime.framework.core.IInputPort; + +/** + * @author Nils Christian Ehmke, Christian Wulf + * + * @since 1.10 + * + * @param <I> + * The type of the input ports + */ +public class PlainTextWebVisualizationSink<T> extends AbstractFilter<PlainTextWebVisualizationSink<T>> implements IWebVisualizationSink { + + public final IInputPort<PlainTextWebVisualizationSink<T>, T> INPUT_OBJECT = this.createInputPort(); + + private Object currentObject = "N/A"; + + /** + * @since 1.10 + */ + public String getHeader() { + return ""; + } + + /** + * @since 1.10 + */ + public String getInitialContent() { + return this.currentObject.toString(); + } + + /** + * @since 1.10 + */ + public String getUpdatedContent() { + return this.currentObject.toString(); + } + + /** + * @since 1.10 + */ + @Override + protected boolean execute(final Context<PlainTextWebVisualizationSink<T>> context) { + final T object = context.tryTake(this.INPUT_OBJECT); + if (object == null) { + return false; + } + + this.currentObject = object; + + return true; + } + +} diff --git a/src/main/java/teetime/util/CyclicListIterator.java b/src/main/java/teetime/util/CyclicListIterator.java new file mode 100644 index 00000000..71e70bbb --- /dev/null +++ b/src/main/java/teetime/util/CyclicListIterator.java @@ -0,0 +1,54 @@ +package teetime.util; + +import java.util.Iterator; +import java.util.List; + +/** + * This iterator infinitely iterates over a list and allows the list to be modified without throwing a <code>ConcurrentMOdificationException</code>. + * + * @author Christian Wulf + * + * @param <T> + */ +public class CyclicListIterator<T> implements Iterator<T> { + + private final List<T> list; + // private Iterator<T> iterator; + + private int currentIndex = 0; + + public CyclicListIterator(final List<T> list) { + this.list = list; + // this.iterator = this.list.iterator(); + } + + public boolean hasNext() { + return true; + } + + public T next() { + // if (!this.iterator.hasNext()) { + // this.iterator = this.list.iterator(); + // } + // return this.iterator.next(); + + // the size of the list could have been changed due to + // <li>an index overflow (then restart from index 0), or + // <li>an add() or a remove(), so update the index + this.currentIndex = this.getCurrentIndex(); + final T element = this.list.get(this.currentIndex); + this.currentIndex++; + return element; + } + + public void remove() { + // this.iterator.remove(); + this.currentIndex = this.getCurrentIndex(); + this.list.remove(this.currentIndex); + } + + private int getCurrentIndex() { + return this.currentIndex % this.list.size(); + } + +} diff --git a/src/main/java/teetime/util/MathUtil.java b/src/main/java/teetime/util/MathUtil.java new file mode 100644 index 00000000..71a36bc0 --- /dev/null +++ b/src/main/java/teetime/util/MathUtil.java @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.util; + +import java.util.List; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class MathUtil { + + private MathUtil() { + // utility class + } + + public static double getVariance(final List<Long> values, final long avgValue) { + double sum = 0; + for (final long val : values) { + final long diff = val - avgValue; + sum += (diff * diff) / (values.size() - 1); + } + return sum; + } + + public static double getConfidenceWidth(final double z, final double variance, final long n) { + return z * Math.sqrt(variance / n); + } + + public static double getConfidenceWidth(final double z, final List<Long> values, final long avgValue) { + final double variance = MathUtil.getVariance(values, avgValue); + final double confidenceWidth = MathUtil.getConfidenceWidth(z, variance, values.size()); + return confidenceWidth; + } +} diff --git a/src/main/java/teetime/util/Pair.java b/src/main/java/teetime/util/Pair.java new file mode 100644 index 00000000..2f501805 --- /dev/null +++ b/src/main/java/teetime/util/Pair.java @@ -0,0 +1,25 @@ +package teetime.util; + +public class Pair<F, S> { + + private final F first; + private final S second; + + public Pair(final F first, final S second) { + this.first = first; + this.second = second; + } + + public static <F, S> Pair<F, S> of(final F first, final S second) { + return new Pair<F, S>(first, second); + } + + public F getFirst() { + return this.first; + } + + public S getSecond() { + return this.second; + } + +} diff --git a/src/main/java/teetime/util/StacklessException.java b/src/main/java/teetime/util/StacklessException.java new file mode 100644 index 00000000..8d6be607 --- /dev/null +++ b/src/main/java/teetime/util/StacklessException.java @@ -0,0 +1,12 @@ +package teetime.util; + +public class StacklessException extends RuntimeException { + + private static final long serialVersionUID = -9040980547278981254L; + + @Override + public synchronized Throwable fillInStackTrace() { // greatly improves performance when constructing + return this; + } + +} diff --git a/src/main/java/teetime/util/StatisticsUtil.java b/src/main/java/teetime/util/StatisticsUtil.java new file mode 100644 index 00000000..48a023b2 --- /dev/null +++ b/src/main/java/teetime/util/StatisticsUtil.java @@ -0,0 +1,112 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.util; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.TimeUnit; + +import teetime.examples.throughput.TimestampObject; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class StatisticsUtil { + + /** + * @since 1.10 + */ + private StatisticsUtil() { + // utility class + } + + public static void calculateAvg(final List<Long> durations) { + + } + + public static void printStatistics(final long overallDurationInNs, final List<TimestampObject> timestampObjects) { + System.out.println("Duration: " + TimeUnit.NANOSECONDS.toMillis(overallDurationInNs) + " ms"); + + final List<Long> sortedDurationsInNs = new ArrayList<Long>(timestampObjects.size() / 2); + long minDurationInNs = Long.MAX_VALUE; + long maxDurationInNs = Long.MIN_VALUE; + long sumInNs = 0; + for (int i = timestampObjects.size() / 2; i < timestampObjects.size(); i++) { + final TimestampObject timestampObject = timestampObjects.get(i); + final long durationInNs = timestampObject.getStopTimestamp() - timestampObject.getStartTimestamp(); + // sortedDurationsInNs.set(i - (timestampObjects.size() / 2), durationInNs); + sortedDurationsInNs.add(durationInNs); + minDurationInNs = Math.min(durationInNs, minDurationInNs); + maxDurationInNs = Math.max(durationInNs, maxDurationInNs); + sumInNs += durationInNs; + } + + final Map<Double, Long> quintileValues = StatisticsUtil.calculateQuintiles(sortedDurationsInNs); + + System.out.println("min: " + TimeUnit.NANOSECONDS.toMicros(minDurationInNs) + " µs"); + System.out.println("max: " + TimeUnit.NANOSECONDS.toMicros(maxDurationInNs) + " µs"); + final long avgDurInNs = sumInNs / (timestampObjects.size() / 2); + System.out.println("avg duration: " + TimeUnit.NANOSECONDS.toMicros(avgDurInNs) + " µs"); + + for (final Entry<Double, Long> entry : quintileValues.entrySet()) { + System.out.println((entry.getKey() * 100) + " % : " + TimeUnit.NANOSECONDS.toMicros(entry.getValue()) + " µs"); + } + + final long confidenceWidthInNs = StatisticsUtil.calculateConfidenceWidth(sortedDurationsInNs, avgDurInNs); + + System.out.println("confidenceWidth: " + confidenceWidthInNs + " ns"); + System.out.println("[" + TimeUnit.NANOSECONDS.toMicros(avgDurInNs - confidenceWidthInNs) + " µs, " + + TimeUnit.NANOSECONDS.toMicros(avgDurInNs + confidenceWidthInNs) + " µs]"); + } + + public static long calculateConfidenceWidth(final List<Long> durations, final long avgDurInNs) { + final double z = 1.96; // for alpha = 0.05 + final double variance = MathUtil.getVariance(durations, avgDurInNs); + final long confidenceWidthInNs = (long) MathUtil.getConfidenceWidth(z, variance, durations.size()); + return confidenceWidthInNs; + } + + public static long calculateConfidenceWidth(final List<Long> durations) { + return StatisticsUtil.calculateConfidenceWidth(durations, StatisticsUtil.calculateAverage(durations)); + } + + public static long calculateAverage(final List<Long> durations) { + long sumNs = 0; + for (final Long value : durations) { + sumNs += value; + } + + return sumNs / durations.size(); + } + + public static Map<Double, Long> calculateQuintiles(final List<Long> durationsInNs) { + Collections.sort(durationsInNs); + + final Map<Double, Long> quintileValues = new LinkedHashMap<Double, Long>(); + final double[] quintiles = { 0.00, 0.25, 0.50, 0.75, 1.00 }; + for (final double quintile : quintiles) { + final int index = (int) ((durationsInNs.size() - 1) * quintile); + quintileValues.put(quintile, durationsInNs.get(index)); + } + return quintileValues; + } +} diff --git a/src/main/java/teetime/util/StopWatch.java b/src/main/java/teetime/util/StopWatch.java new file mode 100644 index 00000000..bd593859 --- /dev/null +++ b/src/main/java/teetime/util/StopWatch.java @@ -0,0 +1,19 @@ +package teetime.util; + +public final class StopWatch { + + private long startTimeInMs; + private long endTimeInMs; + + public final void start() { + this.startTimeInMs = System.nanoTime(); + } + + public final void end() { + this.endTimeInMs = System.nanoTime(); + } + + public final long getDuration() { + return this.endTimeInMs - this.startTimeInMs; + } +} diff --git a/src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java b/src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java new file mode 100644 index 00000000..996f6490 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/P1C1QueueOriginal3.java @@ -0,0 +1,198 @@ +/* + * Copyright 2012 Real Logic Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package teetime.util.concurrent; + +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Queue; +import java.util.concurrent.atomic.AtomicLong; + +/** + * <ul> + * <li>Lock free, observing single writer principal. + * <li>Replacing the long fields with AtomicLong and using lazySet instead of volatile assignment. + * <li>Using the power of 2 mask, forcing the capacity to next power of 2. + * <li>Adding head and tail cache fields. Avoiding redundant volatile reads. + * <li>Padding head/tail AtomicLong fields. Avoiding false sharing. + * <li>Padding head/tail cache fields. Avoiding false sharing. + * </ul> + */ +public final class P1C1QueueOriginal3<E> implements Queue<E> { + private final int capacity; + private final int mask; + private final E[] buffer; + + private final AtomicLong tail = new PaddedAtomicLong(0); + private final AtomicLong head = new PaddedAtomicLong(0); + + public static class PaddedLong { + public long value = 0, p1, p2, p3, p4, p5, p6; + } + + private final PaddedLong tailCache = new PaddedLong(); + private final PaddedLong headCache = new PaddedLong(); + + @SuppressWarnings("unchecked") + public P1C1QueueOriginal3(final int capacity) { + this.capacity = P1C1QueueOriginal3.findNextPositivePowerOfTwo(capacity); + this.mask = this.capacity - 1; + this.buffer = (E[]) new Object[this.capacity]; + } + + public static int findNextPositivePowerOfTwo(final int value) { + return 1 << (32 - Integer.numberOfLeadingZeros(value - 1)); + } + + public boolean add(final E e) { + if (this.offer(e)) { + return true; + } + + throw new IllegalStateException("Queue is full"); + } + + public boolean offer(final E e) { + if (null == e) { + throw new NullPointerException("Null is not a valid element"); + } + + final long currentTail = this.tail.get(); + final long wrapPoint = currentTail - this.capacity; + if (this.headCache.value <= wrapPoint) { + this.headCache.value = this.head.get(); + if (this.headCache.value <= wrapPoint) { + return false; + } + } + + this.buffer[(int) currentTail & this.mask] = e; + this.tail.lazySet(currentTail + 1); + + return true; + } + + public E poll() { + final long currentHead = this.head.get(); + if (currentHead >= this.tailCache.value) { + this.tailCache.value = this.tail.get(); + if (currentHead >= this.tailCache.value) { + return null; + } + } + + final int index = (int) currentHead & this.mask; + final E e = this.buffer[index]; + this.buffer[index] = null; + this.head.lazySet(currentHead + 1); + + return e; + } + + public E remove() { + final E e = this.poll(); + if (null == e) { + throw new NoSuchElementException("Queue is empty"); + } + + return e; + } + + public E element() { + final E e = this.peek(); + if (null == e) { + throw new NoSuchElementException("Queue is empty"); + } + + return e; + } + + public E peek() { + return this.buffer[(int) this.head.get() & this.mask]; + } + + public int size() { + return (int) (this.tail.get() - this.head.get()); + } + + public boolean isEmpty() { + return this.tail.get() == this.head.get(); + } + + public boolean contains(final Object o) { + if (null == o) { + return false; + } + + for (long i = this.head.get(), limit = this.tail.get(); i < limit; i++) { + final E e = this.buffer[(int) i & this.mask]; + if (o.equals(e)) { + return true; + } + } + + return false; + } + + public Iterator<E> iterator() { + throw new UnsupportedOperationException(); + } + + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + public <T> T[] toArray(final T[] a) { + throw new UnsupportedOperationException(); + } + + public boolean remove(final Object o) { + throw new UnsupportedOperationException(); + } + + public boolean containsAll(final Collection<?> c) { + for (final Object o : c) { + if (!this.contains(o)) { + return false; + } + } + + return true; + } + + public boolean addAll(final Collection<? extends E> c) { + for (final E e : c) { + this.add(e); + } + + return true; + } + + public boolean removeAll(final Collection<?> c) { + throw new UnsupportedOperationException(); + } + + public boolean retainAll(final Collection<?> c) { + throw new UnsupportedOperationException(); + } + + public void clear() { + Object value; + do { + value = this.poll(); + } while (null != value); + } +} diff --git a/src/main/java/teetime/util/concurrent/PaddedAtomicLong.java b/src/main/java/teetime/util/concurrent/PaddedAtomicLong.java new file mode 100644 index 00000000..eeffdaf2 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/PaddedAtomicLong.java @@ -0,0 +1,28 @@ +/* + * Copyright 2012 Real Logic Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package teetime.util.concurrent; + +import java.util.concurrent.atomic.AtomicLong; + +public class PaddedAtomicLong extends AtomicLong { + public PaddedAtomicLong() {} + + public PaddedAtomicLong(final long initialValue) { + super(initialValue); + } + + public volatile long p1, p2, p3, p4, p5, p6 = 7; +} diff --git a/src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java b/src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java new file mode 100644 index 00000000..6ddaefc5 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/hashmap/ConcurrentHashMapWithDefault.java @@ -0,0 +1,57 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.util.concurrent.hashmap; + +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ConcurrentHashMapWithDefault<K, V> extends ConcurrentHashMap<K, V> { + + private static final long serialVersionUID = -7958038532219740472L; + + private final ValueFactory<V> valueFactory; + + /** + * @since 1.10 + */ + public ConcurrentHashMapWithDefault(final ValueFactory<V> valueFactory) { + this.valueFactory = valueFactory; + } + + /** + * @return the corresponding value if the key exists. Otherwise, it creates, + * inserts, and returns a new default value. + */ + @SuppressWarnings("unchecked") + @Override + public V get(final Object key) { + V value = super.get(key); + if (value == null) { + synchronized (this) { + value = super.get(key); + if (value == null) { // NOCS (DCL) + value = this.valueFactory.create(); + super.put((K) key, value); + } + } + } + return value; + } +} diff --git a/src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java b/src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java new file mode 100644 index 00000000..52184ddd --- /dev/null +++ b/src/main/java/teetime/util/concurrent/hashmap/TraceBuffer.java @@ -0,0 +1,152 @@ +package teetime.util.concurrent.hashmap; + +import java.io.Serializable; +import java.util.Comparator; +import java.util.SortedSet; +import java.util.TreeSet; + +import kieker.analysis.plugin.filter.flow.TraceEventRecords; +import kieker.common.logging.Log; +import kieker.common.logging.LogFactory; +import kieker.common.record.flow.trace.AbstractTraceEvent; +import kieker.common.record.flow.trace.TraceMetadata; +import kieker.common.record.flow.trace.operation.AfterOperationEvent; +import kieker.common.record.flow.trace.operation.AfterOperationFailedEvent; +import kieker.common.record.flow.trace.operation.BeforeOperationEvent; + +/** + * The TraceBuffer is synchronized to prevent problems with concurrent access. + * + * @author Jan Waller + */ +public final class TraceBuffer implements ValueFactory<TraceBuffer> { + private static final Log LOG = LogFactory.getLog(TraceBuffer.class); + private static final Comparator<AbstractTraceEvent> COMPARATOR = new TraceEventComperator(); + + private TraceMetadata trace; + private final SortedSet<AbstractTraceEvent> events = new TreeSet<AbstractTraceEvent>(COMPARATOR); + + private boolean closeable; + private boolean damaged; + private int openEvents; + private int maxOrderIndex = -1; + + private long minLoggingTimestamp = Long.MAX_VALUE; + private long maxLoggingTimestamp = -1; + + private long traceId = -1; + + /** + * Creates a new instance of this class. + */ + public TraceBuffer() { + // default empty constructor + } + + public void insertEvent(final AbstractTraceEvent event) { + final long myTraceId = event.getTraceId(); + synchronized (this) { + if (this.traceId == -1) { + this.traceId = myTraceId; + } else if (this.traceId != myTraceId) { + LOG.error("Invalid traceId! Expected: " + this.traceId + " but found: " + myTraceId + " in event " + event.toString()); + this.damaged = true; + } + final long loggingTimestamp = event.getTimestamp(); + if (loggingTimestamp > this.maxLoggingTimestamp) { + this.maxLoggingTimestamp = loggingTimestamp; + } + if (loggingTimestamp < this.minLoggingTimestamp) { + this.minLoggingTimestamp = loggingTimestamp; + } + final int orderIndex = event.getOrderIndex(); + if (orderIndex > this.maxOrderIndex) { + this.maxOrderIndex = orderIndex; + } + if (event instanceof BeforeOperationEvent) { + if (orderIndex == 0) { + this.closeable = true; + } + this.openEvents++; + } else if (event instanceof AfterOperationEvent) { + this.openEvents--; + } else if (event instanceof AfterOperationFailedEvent) { + this.openEvents--; + } + if (!this.events.add(event)) { + LOG.error("Duplicate entry for orderIndex " + orderIndex + " with traceId " + myTraceId); + this.damaged = true; + } + } + } + + public void setTrace(final TraceMetadata trace) { + final long myTraceId = trace.getTraceId(); + synchronized (this) { + if (this.traceId == -1) { + this.traceId = myTraceId; + } else if (this.traceId != myTraceId) { + LOG.error("Invalid traceId! Expected: " + this.traceId + " but found: " + myTraceId + " in trace " + trace.toString()); + this.damaged = true; + } + if (this.trace == null) { + this.trace = trace; + } else { + LOG.error("Duplicate Trace entry for traceId " + myTraceId); + this.damaged = true; + } + } + } + + public boolean isFinished() { + synchronized (this) { + return this.closeable && !this.isInvalid(); + } + } + + public boolean isInvalid() { + synchronized (this) { + return (this.trace == null) || this.damaged || (this.openEvents != 0) || (((this.maxOrderIndex + 1) != this.events.size()) || this.events.isEmpty()); + } + } + + public TraceEventRecords toTraceEvents() { + synchronized (this) { + return new TraceEventRecords(this.trace, this.events.toArray(new AbstractTraceEvent[this.events.size()])); + } + } + + public long getMaxLoggingTimestamp() { + synchronized (this) { + return this.maxLoggingTimestamp; + } + } + + public long getMinLoggingTimestamp() { + synchronized (this) { + return this.minLoggingTimestamp; + } + } + + /** + * @author Jan Waller + */ + private static final class TraceEventComperator implements Comparator<AbstractTraceEvent>, Serializable { + private static final long serialVersionUID = 8920737343446332517L; + + /** + * Creates a new instance of this class. + */ + public TraceEventComperator() { + // default empty constructor + } + + public int compare(final AbstractTraceEvent o1, final AbstractTraceEvent o2) { + return o1.getOrderIndex() - o2.getOrderIndex(); + } + } + + public TraceBuffer create() { + return new TraceBuffer(); + } +} diff --git a/src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java b/src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java new file mode 100644 index 00000000..3cca62d6 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/hashmap/ValueFactory.java @@ -0,0 +1,31 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.util.concurrent.hashmap; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public interface ValueFactory<T> { + + /** + * Create a new instance of the type <code>T</code>. + * + * @since 1.10 + */ + public T create(); +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/CircularArray.java b/src/main/java/teetime/util/concurrent/workstealing/CircularArray.java new file mode 100644 index 00000000..d1daf143 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/CircularArray.java @@ -0,0 +1,79 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing; + +import java.util.Arrays; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + * + * @param <T> + */ +public class CircularArray<T> { + + private final long logSize; + private final T[] segment; + + /** + * + * @param logSize + * The initial size of this array in log2, i.e., the number of bits to use + */ + @SuppressWarnings("unchecked") + public CircularArray(final long logSize) { + this.logSize = logSize; + this.segment = (T[]) new Object[1 << this.logSize]; + } + + public long getCapacity() { + return 1 << this.logSize; + } + + public T get(final long i) { + return this.segment[(int) (i % this.getCapacity())]; // risk of overflow + } + + public void put(final long i, final T o) { + this.segment[(int) (i % this.getCapacity())] = o; // risk of overflow + } + + public CircularArray<T> grow(final long b, final long t) { + final CircularArray<T> a = new CircularArray<T>(this.logSize + 1); + for (long i = t; i < b; i++) { + a.put(i, this.get(i)); + } + return a; + } + + public CircularArray<T> shrink(final long b, final long t) { + final CircularArray<T> a = new CircularArray<T>(this.logSize - 1); + for (long i = t; i < b; i++) { + a.put(i, this.get(i)); + } + return a; + } + + @Override + public String toString() { + return Arrays.toString(this.segment); + } +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java new file mode 100644 index 00000000..3be66870 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/CircularWorkStealingDeque.java @@ -0,0 +1,311 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing; + +import java.util.List; +import java.util.concurrent.atomic.AtomicLong; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + */ +public class CircularWorkStealingDeque<T> { + + public static class DequeIsEmptyException extends DequePopException { + private static final long serialVersionUID = -6685406255103741724L; + } + + public static final DequeIsEmptyException DEQUE_IS_EMPTY_EXCEPTION = new DequeIsEmptyException(); + + public static class OperationAbortedException extends DequePopException { + private static final long serialVersionUID = 2983001853326344073L; + } + + public static final OperationAbortedException OPERATION_ABORTED_EXCEPTION = new OperationAbortedException(); + + private static final long LOG_INITIAL_SIZE = 10; + + private volatile long bottom = 0; + private final AtomicLong top = new AtomicLong(); + private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE); + + private boolean casTop(final long oldVal, final long newVal) { + return this.top.compareAndSet(oldVal, newVal); + } + + /** + * + * @param o + * a non-<code>null</code> element + */ + public void pushBottom(final T o) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<T> a = this.activeArray; + final int numElementsToPush = 1; + final long currentSize = b - t; + final long newSize = currentSize + numElementsToPush; + if (newSize > a.getCapacity()) { + a = a.grow(b, t); + this.activeArray = a; + } + a.put(b, o); + this.bottom = b + numElementsToPush; + } + + /** + * + * @param elements + * a non-<code>null</code> list + */ + public void pushBottomMultiple(final List<T> elements) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<T> a = this.activeArray; + final int numElementsToPush = elements.size(); + final long currentSize = b - t; + final long newSize = currentSize + numElementsToPush; + if (newSize > a.getCapacity()) { + a = a.grow(b, t); + this.activeArray = a; + } + + for (final T elem : elements) { + a.put(b, elem); + } + + this.bottom = b + numElementsToPush; + } + + /** + * Returns and removes the latest element from this deque. + * + * @return + * <ul> + * <li><code>null</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + */ + public T popBottom() { + long b = this.bottom; + final CircularArray<T> a = this.activeArray; + b = b - 1; + this.bottom = b; + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + return this.empty(); + } + T o = this.regular(a.get(b)); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + if (!this.casTop(t, t + 1)) { + o = this.empty(); + } + this.bottom = t + 1; + return o; + } + + /** + * Returns and removes the latest element from this deque. + * + * @return <i>the latest element</i>, otherwise it throws a <code>DequeIsEmptyException</code> + * + * @throws DequeIsEmptyException + */ + public T popBottomEx() { + long b = this.bottom; + final CircularArray<T> a = this.activeArray; + b = b - 1; + this.bottom = b; + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + return this.emptyEx(); + } + T o = this.regular(a.get(b)); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + if (!this.casTop(t, t + 1)) { + o = this.emptyEx(); + } + this.bottom = t + 1; + return o; + } + + private void perhapsShrink(final long b, final long t) { + long temp = t; + final CircularArray<T> a = this.activeArray; + if ((b - temp) < (a.getCapacity() / 4)) { + final CircularArray<T> aa = a.shrink(b, temp); + this.activeArray = aa; + final long ss = aa.getCapacity(); + this.bottom = b + ss; + temp = this.top.get(); + if (!this.casTop(temp, temp + ss)) { + this.bottom = b; + // a.free(); + } + } + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return + * <ul> + * <li><code>null</code> if the deque contains no elements, + * <li>(and also) <code>null</code> if the deque is currently being stolen by another thread, + * <li><i>the oldest element</i> otherwise + * </ul> + */ + public T steal() { + final long t = this.top.get(); + final CircularArray<T> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + return this.empty(); + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + return this.empty(); + } else { + return this.abort(); + } + } + final T o = this.regular(a.get(t)); + if (!this.casTop(t, t + 1)) { + return this.abort(); + } + return o; + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return <i>the oldest element</i>, otherwise it throws a <code>DequeIsEmptyException</code> or a <code>OperationAbortedException</code> + * + * @throws DequeIsEmptyException + * @throws OperationAbortedException + */ + public T stealEx() { + final long t = this.top.get(); + final CircularArray<T> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + return this.emptyEx(); + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + return this.emptyEx(); + } else { + return this.abortEx(); + } + } + final T o = this.regular(a.get(t)); + if (!this.casTop(t, t + 1)) { + return this.abortEx(); + } + return o; + } + + private T empty() { + return null; + } + + private T emptyEx() { + throw DEQUE_IS_EMPTY_EXCEPTION; + } + + private T abort() { + return null; + } + + private T abortEx() { + throw OPERATION_ABORTED_EXCEPTION; + } + + private T regular(final T value) { + return value; + } + + /** + * Returns but does not remove the latest element from this deque.<br> + * <i>For debugging purposes</i> + * + * @return <ul> + * <li><code>null</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + */ + public T readBottom() { + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final T o = a.get(b); + return o; + } + + // bottom: 4093 + // bottom: 66429 + // bottom: 29993 + // bottom: 29992 + // + // + // bottom: 4093 + // bottom: 66429 + // bottom: 30008 + // bottom: 30007 + + public boolean isEmpty() { + final long t = this.top.get(); + final long b = this.bottom; + return t >= b; + } + + /** + * For debugging purposes + * + * @return the number of elements this deque contains + */ + public long size(final Object sourceStage) { + final long t = this.top.get(); + final long b = this.bottom; + final long size = b - t; + System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom); + return size; + } + + @Override + public String toString() { + return this.activeArray.toString(); + } + +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/DequePopException.java b/src/main/java/teetime/util/concurrent/workstealing/DequePopException.java new file mode 100644 index 00000000..11fc8b07 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/DequePopException.java @@ -0,0 +1,8 @@ +package teetime.util.concurrent.workstealing; + +import teetime.util.StacklessException; + +public class DequePopException extends StacklessException { + private static final long serialVersionUID = 496512683536868149L; + +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java new file mode 100644 index 00000000..854839c5 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithSentinel.java @@ -0,0 +1,202 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing.alternative; + +import java.util.concurrent.atomic.AtomicLong; + +import teetime.util.concurrent.workstealing.CircularArray; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + */ +public class CircularWorkStealingDequeWithSentinel<T> { + + public static enum State { + REGULAR, EMPTY, ABORT + } + + public static class ReturnValue<T> { + private final State state; + private final T value; + + public ReturnValue(final State state, final T value) { + this.state = state; + this.value = value; + } + + public T getValue() { + return this.value; + } + + public State getState() { + return this.state; + } + } + + private static final long LOG_INITIAL_SIZE = 10; + + private volatile long bottom = 0; + // private volatile long top = 0; + private final AtomicLong top = new AtomicLong(); + private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE); + + private boolean casTop(final long oldVal, final long newVal) { + // boolean preCond; + // synchronized (this) { + // preCond = (this.top == oldVal); + // if (preCond) { + // this.top = newVal; + // } + // } + // return preCond; + return this.top.compareAndSet(oldVal, newVal); + } + + public void pushBottom(final T o) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size > (a.getCapacity() - 1)) { + a = a.grow(b, t); + this.activeArray = a; + } + a.put(b, o); + this.bottom = b + 1; + } + + /** + * + * @return + * <ul> + * <li><code>empty()</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + */ + public ReturnValue<T> popBottom() { + long b = this.bottom; + final CircularArray<T> a = this.activeArray; + b = b - 1; + this.bottom = b; + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + return new ReturnValue<T>(State.EMPTY, null); + } + ReturnValue<T> o = new ReturnValue<T>(State.REGULAR, a.get(b)); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + if (!this.casTop(t, t + 1)) { + o = new ReturnValue<T>(State.EMPTY, null); + } + this.bottom = t + 1; + return o; + } + + void perhapsShrink(final long b, final long t) { + long temp = t; + final CircularArray<T> a = this.activeArray; + if ((b - temp) < (a.getCapacity() / 4)) { + final CircularArray<T> aa = a.shrink(b, temp); + this.activeArray = aa; + final long ss = aa.getCapacity(); + this.bottom = b + ss; + temp = this.top.get(); + if (!this.casTop(temp, temp + ss)) { + this.bottom = b; + // a.free(); + } + } + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return + * <ul> + * <li><code>empty()</code> if the deque contains no elements, + * <li><code>abort()</code> if the deque is currently being stolen by another thread, + * <li><i>the oldest element</i> otherwise + * </ul> + */ + public ReturnValue<T> steal() { + final long t = this.top.get(); + final CircularArray<T> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + return new ReturnValue<T>(State.EMPTY, null); + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + return new ReturnValue<T>(State.EMPTY, null); + } else { + return new ReturnValue<T>(State.ABORT, null); + } + } + final ReturnValue<T> o = new ReturnValue<T>(State.REGULAR, a.get(t)); + if (!this.casTop(t, t + 1)) { + return new ReturnValue<T>(State.ABORT, null); + } + return o; + } + + /** + * For debugging purposes + * + * @return but does not remove the bottom element from this deque + */ + public T readBottom() { + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final T o = a.get(b); + return o; + } + + // bottom: 4093 + // bottom: 66429 + // bottom: 29993 + // bottom: 29992 + // + // + // bottom: 4093 + // bottom: 66429 + // bottom: 30008 + // bottom: 30007 + + /** + * For debugging purposes + * + * @return the number of elements this deque contains + */ + public long size(final Object sourceStage) { + final long t = this.top.get(); + final long b = this.bottom; + final long size = b - t; + System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom); + return size; + } +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java new file mode 100644 index 00000000..746765a1 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeWithThreadLocalSentinel.java @@ -0,0 +1,226 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing.alternative; + +import java.util.concurrent.atomic.AtomicLong; + +import teetime.util.concurrent.workstealing.CircularArray; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + */ +public class CircularWorkStealingDequeWithThreadLocalSentinel<T> { + + public static enum State { + REGULAR, EMPTY, ABORT + } + + public static class ReturnValue<T> { + private State state; + private T value; + + public State getState() { + return this.state; + } + + public T getValue() { + return this.value; + } + + public ReturnValue<T> setState(final State state) { + this.state = state; + return this; + } + + public ReturnValue<T> setStateAndValue(final State state, final T value) { + this.state = state; + this.value = value; + return this; + } + } + + private final ThreadLocal<ReturnValue<T>> returnValue = new ThreadLocal<ReturnValue<T>>(); + + private static final long LOG_INITIAL_SIZE = 10; + + private volatile long bottom = 0; + // private volatile long top = 0; + private final AtomicLong top = new AtomicLong(); + private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE); + + public CircularWorkStealingDequeWithThreadLocalSentinel() { + this.returnValue.set(new ReturnValue<T>()); + } + + private boolean casTop(final long oldVal, final long newVal) { + // boolean preCond; + // synchronized (this) { + // preCond = (this.top == oldVal); + // if (preCond) { + // this.top = newVal; + // } + // } + // return preCond; + return this.top.compareAndSet(oldVal, newVal); + } + + public void pushBottom(final T o) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size > (a.getCapacity() - 1)) { + a = a.grow(b, t); + this.activeArray = a; + } + a.put(b, o); + this.bottom = b + 1; + } + + /** + * + * @return + * <ul> + * <li><code>empty()</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + */ + public ReturnValue<T> popBottom() { + long b = this.bottom; + final CircularArray<T> a = this.activeArray; + b = b - 1; + this.bottom = b; + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + return this.empty(); + } + ReturnValue<T> o = this.regular(a.get(b)); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + if (!this.casTop(t, t + 1)) { + o = this.empty(); + } + this.bottom = t + 1; + return o; + } + + void perhapsShrink(final long b, final long t) { + long temp = t; + final CircularArray<T> a = this.activeArray; + if ((b - temp) < (a.getCapacity() / 4)) { + final CircularArray<T> aa = a.shrink(b, temp); + this.activeArray = aa; + final long ss = aa.getCapacity(); + this.bottom = b + ss; + temp = this.top.get(); + if (!this.casTop(temp, temp + ss)) { + this.bottom = b; + // a.free(); + } + } + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return + * <ul> + * <li><code>empty()</code> if the deque contains no elements, + * <li><code>abort()</code> if the deque is currently being stolen by another thread, + * <li><i>the oldest element</i> otherwise + * </ul> + */ + public ReturnValue<T> steal() { + final long t = this.top.get(); + final CircularArray<T> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + return this.empty(); + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + return this.empty(); + } else { + return this.abort(); + } + } + final ReturnValue<T> o = this.regular(a.get(t)); + if (!this.casTop(t, t + 1)) { + return this.abort(); + } + return o; + } + + private ReturnValue<T> empty() { + return this.returnValue.get().setState(State.EMPTY); + } + + private ReturnValue<T> abort() { + return this.returnValue.get().setState(State.ABORT); + } + + private ReturnValue<T> regular(final T value) { + return this.returnValue.get().setStateAndValue(State.REGULAR, value); + } + + /** + * For debugging purposes + * + * @return but does not remove the bottom element from this deque + */ + public T readBottom() { + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final T o = a.get(b); + return o; + } + + // bottom: 4093 + // bottom: 66429 + // bottom: 29993 + // bottom: 29992 + // + // + // bottom: 4093 + // bottom: 66429 + // bottom: 30008 + // bottom: 30007 + + /** + * For debugging purposes + * + * @return the number of elements this deque contains + */ + public long size(final Object sourceStage) { + final long t = this.top.get(); + final long b = this.bottom; + final long size = b - t; + System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom); + return size; + } +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java new file mode 100644 index 00000000..bb227e1c --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/ExceptionalCircularWorkStealingDeque.java @@ -0,0 +1,196 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing.alternative; + +import java.util.concurrent.atomic.AtomicLong; + +import teetime.util.concurrent.workstealing.CircularArray; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + */ +public class ExceptionalCircularWorkStealingDeque<T> { + + public static class DequeIsEmptyException extends Exception { + private static final long serialVersionUID = -6685406255103741724L; + } + + public static final DequeIsEmptyException DEQUE_IS_EMPTY_EXCEPTION = new DequeIsEmptyException(); + + public static class OperationAbortedException extends Exception { + private static final long serialVersionUID = 2983001853326344073L; + } + + public static final OperationAbortedException OPERATION_ABORTED_EXCEPTION = new OperationAbortedException(); + + private static final long LOG_INITIAL_SIZE = 10; + + private volatile long bottom = 0; + // private volatile long top = 0; + private final AtomicLong top = new AtomicLong(); + private volatile CircularArray<T> activeArray = new CircularArray<T>(LOG_INITIAL_SIZE); + + private boolean casTop(final long oldVal, final long newVal) { + // boolean preCond; + // synchronized (this) { + // preCond = (this.top == oldVal); + // if (preCond) { + // this.top = newVal; + // } + // } + // return preCond; + return this.top.compareAndSet(oldVal, newVal); + } + + public void pushBottom(final T o) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size > (a.getCapacity() - 1)) { + a = a.grow(b, t); + this.activeArray = a; + } + a.put(b, o); + this.bottom = b + 1; + } + + /** + * + * @return + * <ul> + * <li><code>EMPTY</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + * @throws DequeIsEmptyException + */ + public T popBottom() throws DequeIsEmptyException { + long b = this.bottom; + final CircularArray<T> a = this.activeArray; + b = b - 1; + this.bottom = b; + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + throw DEQUE_IS_EMPTY_EXCEPTION; + } + final T o = a.get(b); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + final boolean success = this.casTop(t, t + 1); + this.bottom = t + 1; + if (!success) { + throw DEQUE_IS_EMPTY_EXCEPTION; + } + return o; + } + + void perhapsShrink(final long b, final long t) { + long temp = t; + final CircularArray<T> a = this.activeArray; + if ((b - temp) < (a.getCapacity() / 4)) { + final CircularArray<T> aa = a.shrink(b, temp); + this.activeArray = aa; + final long ss = aa.getCapacity(); + this.bottom = b + ss; + temp = this.top.get(); + if (!this.casTop(temp, temp + ss)) { + this.bottom = b; + // a.free(); + } + } + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return + * <ul> + * <li><code>EMPTY</code> if the deque contains no elements, + * <li><code>ABORT</code> if the deque is currently being stolen by another thread, + * <li><i>the oldest element</i> otherwise + * </ul> + * @throws DequeIsEmptyException + * @throws OperationAbortedException + */ + public T steal() throws DequeIsEmptyException, OperationAbortedException { + final long t = this.top.get(); + final CircularArray<T> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + throw DEQUE_IS_EMPTY_EXCEPTION; + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + throw DEQUE_IS_EMPTY_EXCEPTION; + } else { + throw OPERATION_ABORTED_EXCEPTION; + } + } + final T o = a.get(t); + if (!this.casTop(t, t + 1)) { + throw OPERATION_ABORTED_EXCEPTION; + } + return o; + } + + /** + * For debugging purposes + * + * @return but does not remove the bottom element from this deque + */ + public T readBottom() { + final long b = this.bottom; + final CircularArray<T> a = this.activeArray; + final T o = a.get(b); + return o; + } + + // bottom: 4093 + // bottom: 66429 + // bottom: 29993 + // bottom: 29992 + // + // + // bottom: 4093 + // bottom: 66429 + // bottom: 30008 + // bottom: 30007 + + /** + * For debugging purposes + * + * @return the number of elements this deque contains + */ + public long size(final Object sourceStage) { + final long t = this.top.get(); + final long b = this.bottom; + final long size = b - t; + System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom); + return size; + } +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java new file mode 100644 index 00000000..1ece4ff8 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedCircularWorkStealingDeque.java @@ -0,0 +1,182 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing.alternative; + +import java.util.concurrent.atomic.AtomicLong; + +import teetime.util.concurrent.workstealing.CircularArray; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + */ +public class UntypedCircularWorkStealingDeque { + public static final Object EMPTY = new Object(); + public static final Object ABORT = new Object(); + + private static final long LOG_INITIAL_SIZE = 10; + + private volatile long bottom = 0; + // private volatile long top = 0; + private final AtomicLong top = new AtomicLong(); + private volatile CircularArray<Object> activeArray = new CircularArray<Object>(LOG_INITIAL_SIZE); + + private boolean casTop(final long oldVal, final long newVal) { + // boolean preCond; + // synchronized (this) { + // preCond = (this.top == oldVal); + // if (preCond) { + // this.top = newVal; + // } + // } + // return preCond; + return this.top.compareAndSet(oldVal, newVal); + } + + public void pushBottom(final Object o) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<Object> a = this.activeArray; + final long size = b - t; + if (size > (a.getCapacity() - 1)) { + a = a.grow(b, t); + this.activeArray = a; + } + a.put(b, o); + this.bottom = b + 1; + } + + /** + * + * @return + * <ul> + * <li><code>EMPTY</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + */ + public Object popBottom() { + long b = this.bottom; + final CircularArray<Object> a = this.activeArray; + b = b - 1; + this.bottom = b; // reserve (avoid stealing) the current bottom element + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + return EMPTY; + } + Object o = a.get(b); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + if (!this.casTop(t, t + 1)) { + o = EMPTY; + } + this.bottom = t + 1; + return o; + } + + void perhapsShrink(final long b, final long t) { + long temp = t; + final CircularArray<Object> a = this.activeArray; + if ((b - temp) < (a.getCapacity() / 4)) { + final CircularArray<Object> aa = a.shrink(b, temp); + this.activeArray = aa; + final long ss = aa.getCapacity(); + this.bottom = b + ss; + temp = this.top.get(); + if (!this.casTop(temp, temp + ss)) { + this.bottom = b; + // a.free(); + } + } + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return + * <ul> + * <li><code>EMPTY</code> if the deque contains no elements, + * <li><code>ABORT</code> if the deque is currently being stolen by another thread, + * <li><i>the oldest element</i> otherwise + * </ul> + */ + public Object steal() { + final long t = this.top.get(); + final CircularArray<Object> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<Object> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + return EMPTY; + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + return EMPTY; + } else { + return ABORT; + } + } + final Object o = a.get(t); + if (!this.casTop(t, t + 1)) { + return ABORT; + } + return o; + } + + /** + * For debugging purposes + * + * @return but does not remove the bottom element from this deque + */ + public Object readBottom() { + final long b = this.bottom; + final CircularArray<Object> a = this.activeArray; + final Object o = a.get(b); + return o; + } + + // bottom: 4093 + // bottom: 66429 + // bottom: 29993 + // bottom: 29992 + // + // + // bottom: 4093 + // bottom: 66429 + // bottom: 30008 + // bottom: 30007 + + /** + * For debugging purposes + * + * @return the number of elements this deque contains + */ + public long size(final Object sourceStage) { + final long t = this.top.get(); + final long b = this.bottom; + final long size = b - t; + System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom); + return size; + } +} diff --git a/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java new file mode 100644 index 00000000..f72634b2 --- /dev/null +++ b/src/main/java/teetime/util/concurrent/workstealing/alternative/UntypedExceptionalCircularWorkStealingDeque.java @@ -0,0 +1,195 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ + +package teetime.util.concurrent.workstealing.alternative; + +import java.util.concurrent.atomic.AtomicLong; + +import teetime.util.concurrent.workstealing.CircularArray; +import teetime.util.concurrent.workstealing.DequePopException; + +/** + * + * @author Christian Wulf + * + * @see "Dynamic Circular WorkStealing Deque" + * + * @since 1.10 + */ +public class UntypedExceptionalCircularWorkStealingDeque { + public static class DequeIsEmptyException extends DequePopException { + private static final long serialVersionUID = -6685406255103741724L; + } + + public static final DequeIsEmptyException DEQUE_IS_EMPTY_EXCEPTION = new DequeIsEmptyException(); + + public static class OperationAbortedException extends DequePopException { + private static final long serialVersionUID = 2983001853326344073L; + } + + public static final OperationAbortedException OPERATION_ABORTED_EXCEPTION = new OperationAbortedException(); + + private static final long LOG_INITIAL_SIZE = 10; + + private volatile long bottom = 0; + // private volatile long top = 0; + private final AtomicLong top = new AtomicLong(); + private volatile CircularArray<Object> activeArray = new CircularArray<Object>(LOG_INITIAL_SIZE); + + private boolean casTop(final long oldVal, final long newVal) { + // boolean preCond; + // synchronized (this) { + // preCond = (this.top == oldVal); + // if (preCond) { + // this.top = newVal; + // } + // } + // return preCond; + return this.top.compareAndSet(oldVal, newVal); + } + + public void pushBottom(final Object o) { + final long b = this.bottom; + final long t = this.top.get(); + CircularArray<Object> a = this.activeArray; + final long size = b - t; + if (size > (a.getCapacity() - 1)) { + a = a.grow(b, t); + this.activeArray = a; + } + a.put(b, o); + this.bottom = b + 1; + } + + /** + * + * @return + * <ul> + * <li><code>EMPTY</code> if the deque contains no elements, + * <li><i>the latest element</i> otherwise + * </ul> + * @throws DequeIsEmptyException + */ + public Object popBottom() throws DequePopException { + long b = this.bottom; + final CircularArray<Object> a = this.activeArray; + b = b - 1; + this.bottom = b; + final long t = this.top.get(); + final long size = b - t; + if (size < 0) { + this.bottom = t; + throw DEQUE_IS_EMPTY_EXCEPTION; + } + final Object o = a.get(b); + if (size > 0) { + this.perhapsShrink(b, t); + return o; + } + final boolean success = this.casTop(t, t + 1); + this.bottom = t + 1; + if (!success) { + throw DEQUE_IS_EMPTY_EXCEPTION; + } + return o; + } + + void perhapsShrink(final long b, final long t) { + long temp = t; + final CircularArray<Object> a = this.activeArray; + if ((b - temp) < (a.getCapacity() / 4)) { + final CircularArray<Object> aa = a.shrink(b, temp); + this.activeArray = aa; + final long ss = aa.getCapacity(); + this.bottom = b + ss; + temp = this.top.get(); + if (!this.casTop(temp, temp + ss)) { + this.bottom = b; + // a.free(); + } + } + } + + /** + * Tries to steal (return & remove) the oldest element from this deque. + * + * @return + * <ul> + * <li><code>EMPTY</code> if the deque contains no elements, + * <li><code>ABORT</code> if the deque is currently being stolen by another thread, + * <li><i>the oldest element</i> otherwise + * </ul> + * @throws DequePopException + */ + public Object steal() throws DequePopException { + final long t = this.top.get(); + final CircularArray<Object> oldArr = this.activeArray; + final long b = this.bottom; + final CircularArray<Object> a = this.activeArray; + final long size = b - t; + if (size <= 0) { + throw DEQUE_IS_EMPTY_EXCEPTION; + } + if ((size % a.getCapacity()) == 0) { + if ((oldArr == a) && (t == this.top.get())) { + throw DEQUE_IS_EMPTY_EXCEPTION; + } else { + throw OPERATION_ABORTED_EXCEPTION; + } + } + final Object o = a.get(t); + if (!this.casTop(t, t + 1)) { + throw OPERATION_ABORTED_EXCEPTION; + } + return o; + } + + /** + * For debugging purposes + * + * @return but does not remove the bottom element from this deque + */ + public Object readBottom() { + final long b = this.bottom; + final CircularArray<Object> a = this.activeArray; + final Object o = a.get(b); + return o; + } + + // bottom: 4093 + // bottom: 66429 + // bottom: 29993 + // bottom: 29992 + // + // + // bottom: 4093 + // bottom: 66429 + // bottom: 30008 + // bottom: 30007 + + /** + * For debugging purposes + * + * @return the number of elements this deque contains + */ + public long size(final Object sourceStage) { + final long t = this.top.get(); + final long b = this.bottom; + final long size = b - t; + System.out.println("sourceStage=" + sourceStage + ", " + "bottom: " + this.bottom); + return size; + } +} diff --git a/src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java b/src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java new file mode 100644 index 00000000..4a7e735e --- /dev/null +++ b/src/test/java/kieker/analysis/stage/EmptyPassOnFilterTest.java @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package kieker.analysis.stage; + +import kieker.analysis.AnalysisController; +import kieker.analysis.IAnalysisController; +import kieker.analysis.exception.AnalysisConfigurationException; +import kieker.analysis.plugin.reader.filesystem.FSReader; +import kieker.analysis.stage.CacheFilter; +import kieker.analysis.stage.EmptyPassOnFilter; +import kieker.common.configuration.Configuration; + +import org.junit.Test; + +/** + * @author Nils Christian Ehmke + * + * @since 1.10 + */ +public class EmptyPassOnFilterTest { + + private static final int NUMBER_OF_EMPTY_PASS_ON_FILTERS = 1000; + + @Test + public void test() throws IllegalStateException, AnalysisConfigurationException { + final IAnalysisController ac = new AnalysisController(); + + final Configuration fsReaderConfiguration = new Configuration(); + fsReaderConfiguration.setProperty(FSReader.CONFIG_PROPERTY_NAME_INPUTDIRS, + "examples/userguide/ch5--trace-monitoring-aspectj/testdata/kieker-20100830-082225522-UTC"); + final FSReader reader = new FSReader(fsReaderConfiguration, ac); + + final CacheFilter cacheFilter = new CacheFilter(new Configuration(), ac); + + EmptyPassOnFilter predecessor = new EmptyPassOnFilter(new Configuration(), ac); + ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, cacheFilter, CacheFilter.INPUT_PORT_NAME); + ac.connect(cacheFilter, CacheFilter.OUTPUT_PORT_NAME, predecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + for (int idx = 0; idx < (NUMBER_OF_EMPTY_PASS_ON_FILTERS - 1); idx++) { + final EmptyPassOnFilter newPredecessor = new EmptyPassOnFilter(new Configuration(), ac); + ac.connect(predecessor, EmptyPassOnFilter.OUTPUT_PORT_NAME, newPredecessor, EmptyPassOnFilter.INPUT_PORT_NAME); + predecessor = newPredecessor; + } + + ac.run(); + } + +} diff --git a/src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java b/src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java new file mode 100644 index 00000000..3b7eb351 --- /dev/null +++ b/src/test/java/teetime/examples/recordReader/RecordReaderAnalysisTest.java @@ -0,0 +1,127 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.recordReader; + +import java.io.File; +import java.util.LinkedList; +import java.util.List; + +import kieker.analysis.AnalysisController; +import kieker.analysis.IAnalysisController; +import kieker.analysis.IProjectContext; +import kieker.analysis.plugin.annotation.InputPort; +import kieker.analysis.plugin.filter.AbstractFilterPlugin; +import kieker.analysis.plugin.filter.forward.CountingFilter; +import kieker.analysis.plugin.filter.forward.ListCollectionFilter; +import kieker.analysis.plugin.reader.filesystem.FSReader; +import kieker.common.configuration.Configuration; +import kieker.common.record.IMonitoringRecord; +import kieker.common.util.registry.Registry; + +import org.junit.Assert; +import org.junit.Test; + +import teetime.stage.kieker.className.ClassNameRegistry; +import teetime.stage.kieker.className.ClassNameRegistryRepository; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class RecordReaderAnalysisTest { + + private final static File INPUT_DIRECTORY = new File("examples/userguide/ch5--trace-monitoring-aspectj/testdata/kieker-20100830-082225522-UTC"); + + @Test + public void testExampleWithNewFramework() { + final List<IMonitoringRecord> records = new LinkedList<IMonitoringRecord>(); + + final RecordReaderAnalysis recordReaderAnalysis = new RecordReaderAnalysis(); + recordReaderAnalysis.init(); + recordReaderAnalysis.setInputFile(RecordReaderAnalysisTest.INPUT_DIRECTORY); + recordReaderAnalysis.setOutputRecordList(records); + recordReaderAnalysis.start(); + + final ClassNameRegistryRepository classNameRegistryRepository = recordReaderAnalysis.getClassNameRegistryRepository(); + Assert.assertEquals(1, classNameRegistryRepository.size()); + + final ClassNameRegistry classNameRegistry = classNameRegistryRepository.get(RecordReaderAnalysisTest.INPUT_DIRECTORY); + Assert.assertNotNull(classNameRegistry); + + Assert.assertEquals(2, classNameRegistry.size()); + Assert.assertEquals("kieker.common.record.misc.KiekerMetadataRecord", classNameRegistry.get(0)); + Assert.assertEquals("kieker.common.record.controlflow.OperationExecutionRecord", classNameRegistry.get(1)); + Assert.assertEquals(6541, records.size()); + Assert.assertEquals(1283156546127117246l, records.get(0).getLoggingTimestamp()); + } + + @Test + public void testExampleWithKiekerFramework() throws Exception { + final IAnalysisController ac = new AnalysisController(); + + final Configuration readerConfiguration = new Configuration(); + readerConfiguration.setProperty(FSReader.CONFIG_PROPERTY_NAME_INPUTDIRS, INPUT_DIRECTORY.getAbsolutePath()); + final FSReader reader = new FSReader(readerConfiguration, ac); + + final ListCollectionFilter<IMonitoringRecord> records = new ListCollectionFilter<IMonitoringRecord>(new Configuration(), ac); + final ClassNameRegistryFilter classNameRegistry = new ClassNameRegistryFilter(new Configuration(), ac); + + final CountingFilter countingFilter = new CountingFilter(new Configuration(), ac); + + ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, countingFilter, CountingFilter.INPUT_PORT_NAME_EVENTS); + ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, records, ListCollectionFilter.INPUT_PORT_NAME); + ac.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, classNameRegistry, ClassNameRegistryFilter.INPUT_PORT_NAME_RECORDS); + + ac.run(); + + // Keep in mind that the metadata record is not processed in a regular way + Assert.assertEquals(1, classNameRegistry.size()); + Assert.assertEquals("kieker.common.record.controlflow.OperationExecutionRecord", classNameRegistry.get(0)); + Assert.assertEquals(6540, countingFilter.getMessageCount()); + Assert.assertEquals(1283156545581511026L, records.getList().get(0).getLoggingTimestamp()); + } + + private static class ClassNameRegistryFilter extends AbstractFilterPlugin { + + public static final String INPUT_PORT_NAME_RECORDS = "input"; + + private final Registry<String> registry = new Registry<String>(); + + public ClassNameRegistryFilter(final Configuration configuration, final IProjectContext projectContext) { + super(configuration, projectContext); + } + + @InputPort(name = ClassNameRegistryFilter.INPUT_PORT_NAME_RECORDS, eventTypes = IMonitoringRecord.class) + public void input(final IMonitoringRecord record) { + this.registry.get(record.getClass().getName()); + } + + public Object get(final int id) { + return this.registry.get(id); + } + + public Object size() { + return this.registry.getSize(); + } + + @Override + public Configuration getCurrentConfiguration() { + return new Configuration(); + } + + } +} diff --git a/src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java b/src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java new file mode 100644 index 00000000..236baae7 --- /dev/null +++ b/src/test/java/teetime/examples/throughput/MethodCallThoughputTimestampAnalysisTest.java @@ -0,0 +1,73 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.throughput; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +import kieker.common.logging.LogFactory; + +import org.junit.Before; +import org.junit.Test; + +import teetime.util.StatisticsUtil; +import teetime.util.StopWatch; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class MethodCallThoughputTimestampAnalysisTest { + + private static final int NUM_OBJECTS_TO_CREATE = 100000; + + @Before + public void before() { + System.setProperty(LogFactory.CUSTOM_LOGGER_JVM, "NONE"); + } + + // 500 times faster than our new framework + // TODO check why + + @Test + public void testWithManyObjects() { + final StopWatch stopWatch = new StopWatch(); + final List<TimestampObject> timestampObjects = new ArrayList<TimestampObject>(NUM_OBJECTS_TO_CREATE); + + final MethodCallThroughputAnalysis analysis = new MethodCallThroughputAnalysis(); + analysis.setNumNoopFilters(800); + analysis.setTimestampObjects(timestampObjects); + analysis.setInput(NUM_OBJECTS_TO_CREATE, new Callable<TimestampObject>() { + @Override + public TimestampObject call() throws Exception { + return new TimestampObject(); + } + }); + analysis.init(); + + stopWatch.start(); + try { + analysis.start(); + } finally { + stopWatch.end(); + } + + StatisticsUtil.printStatistics(stopWatch.getDuration(), timestampObjects); + } + +} diff --git a/src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java b/src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java new file mode 100644 index 00000000..12538bec --- /dev/null +++ b/src/test/java/teetime/examples/throughput/ThroughputAnalysisTest.java @@ -0,0 +1,82 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.throughput; + +import java.util.concurrent.Callable; + +import kieker.common.logging.LogFactory; + +import org.junit.Before; +import org.junit.Test; + +import teetime.util.StopWatch; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ThroughputAnalysisTest { + + private static final int NUM_OBJECTS_TO_CREATE = 100000; + private static final int numRuns = 2; + + @Before + public void before() { + System.setProperty(LogFactory.CUSTOM_LOGGER_JVM, "NONE"); + } + + @Test + public void testWithMultipleRuns() { + final StopWatch stopWatch = new StopWatch(); + final long[] durations = new long[numRuns]; + + for (int i = 0; i < numRuns; i++) { + System.out.println("current run: " + i); + final ThroughputAnalysis<Object> analysis = new ThroughputAnalysis<Object>(); + analysis.setNumNoopFilters(800); + analysis.setInput(NUM_OBJECTS_TO_CREATE, new Callable<Object>() { + @Override + public Object call() throws Exception { + return new Object(); + } + }); + analysis.init(); + + stopWatch.start(); + try { + analysis.start(); + } finally { + stopWatch.end(); + } + + durations[i] = stopWatch.getDuration(); + } + + // for (final long dur : durations) { + // System.out.println("Duration: " + (dur / 1000) + " �s"); + // } + + long sum = 0; + for (int i = durations.length / 2; i < durations.length; i++) { + sum += durations[i]; + } + + final long avgDur = sum / (numRuns / 2); + System.out.println("avg duration: " + (avgDur / 1000) + " �s"); + } + +} diff --git a/src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java b/src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java new file mode 100644 index 00000000..e4289f49 --- /dev/null +++ b/src/test/java/teetime/examples/throughput/ThroughputTimestampAnalysisTest.java @@ -0,0 +1,87 @@ +/*************************************************************************** + * Copyright 2014 Kieker Project (http://kieker-monitoring.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + ***************************************************************************/ +package teetime.examples.throughput; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.Callable; + +import kieker.common.logging.LogFactory; + +import org.junit.Before; +import org.junit.Test; + +import teetime.util.StatisticsUtil; +import teetime.util.StopWatch; + +/** + * @author Christian Wulf + * + * @since 1.10 + */ +public class ThroughputTimestampAnalysisTest { + + private static final int NUM_OBJECTS_TO_CREATE = 100000; + + @Before + public void before() { + System.setProperty(LogFactory.CUSTOM_LOGGER_JVM, "NONE"); + } + + // Using QueuePipes ist 1/3 faster than using MethodCallPipes + // reason: + /* + * MethodCallPipes: + * <ul> + * <li>SchedulingOverhead: 12629 ms + * <li>ExecutedUnsuccessfullyCount: 80300001 + * </ul> + * + * QueuePipes: + * <ul> + * <li>SchedulingOverhead: 11337 ms + * <li>ExecutedUnsuccessfullyCount: 804 + * </ul> + */ + + @Test + public void testWithManyObjects() { + final StopWatch stopWatch = new StopWatch(); + final List<TimestampObject> timestampObjects = new ArrayList<TimestampObject>(NUM_OBJECTS_TO_CREATE); + + final ThroughputTimestampAnalysis analysis = new ThroughputTimestampAnalysis(); + analysis.setShouldUseQueue(true); + analysis.setNumNoopFilters(800); + analysis.setTimestampObjects(timestampObjects); + analysis.setInput(NUM_OBJECTS_TO_CREATE, new Callable<TimestampObject>() { + @Override + public TimestampObject call() throws Exception { + return new TimestampObject(); + } + }); + analysis.init(); + + stopWatch.start(); + try { + analysis.start(); + } finally { + stopWatch.end(); + } + + StatisticsUtil.printStatistics(stopWatch.getDuration(), timestampObjects); + } + +} diff --git a/src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java b/src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java new file mode 100644 index 00000000..9ad2c0b9 --- /dev/null +++ b/src/test/java/teetime/util/concurrent/workstealing/alternative/CircularWorkStealingDequeTest.java @@ -0,0 +1,32 @@ +package teetime.util.concurrent.workstealing.alternative; + +import org.hamcrest.number.OrderingComparison; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import teetime.util.StopWatch; +import teetime.util.concurrent.workstealing.CircularWorkStealingDeque; + +public class CircularWorkStealingDequeTest { + private StopWatch stopWatch; + + @Before + public void before() { + this.stopWatch = new StopWatch(); + } + + @Test + public void measureManyEmptyPulls() { + final CircularWorkStealingDeque<Object> deque = new CircularWorkStealingDeque<Object>(); + + final int numIterations = UntypedCircularWorkStealingDequeTest.NUM_ITERATIONS; + this.stopWatch.start(); + for (int i = 0; i < numIterations; i++) { + deque.popBottom(); + } + this.stopWatch.end(); + + Assert.assertThat(this.stopWatch.getDuration(), OrderingComparison.lessThan(UntypedCircularWorkStealingDequeTest.EXPECTED_DURATION)); + } +} diff --git a/teetime/src/main/java/teetime/App.java b/teetime/src/main/java/teetime/App.java deleted file mode 100644 index f4114b2c..00000000 --- a/teetime/src/main/java/teetime/App.java +++ /dev/null @@ -1,13 +0,0 @@ -package teetime; - -/** - * Hello world! - * - */ -public class App -{ - public static void main( String[] args ) - { - System.out.println( "Hello World!" ); - } -} diff --git a/teetime/src/test/java/teetime/AppTest.java b/teetime/src/test/java/teetime/AppTest.java deleted file mode 100644 index 755e7d8e..00000000 --- a/teetime/src/test/java/teetime/AppTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package teetime; - -import junit.framework.Test; -import junit.framework.TestCase; -import junit.framework.TestSuite; - -/** - * Unit test for simple App. - */ -public class AppTest - extends TestCase -{ - /** - * Create the test case - * - * @param testName name of the test case - */ - public AppTest( String testName ) - { - super( testName ); - } - - /** - * @return the suite of tests being tested - */ - public static Test suite() - { - return new TestSuite( AppTest.class ); - } - - /** - * Rigourous Test :-) - */ - public void testApp() - { - assertTrue( true ); - } -} -- GitLab