Java tutorial
/*************************************************************************** * Copyright 2015 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.tools.traceAnalysis; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; import java.io.Serializable; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.TimeZone; import java.util.TreeSet; import java.util.concurrent.TimeUnit; import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.HelpFormatter; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; import kieker.analysis.AnalysisController; import kieker.analysis.analysisComponent.AbstractAnalysisComponent; import kieker.analysis.exception.AnalysisConfigurationException; import kieker.analysis.plugin.AbstractPlugin; import kieker.analysis.plugin.filter.flow.EventRecordTraceReconstructionFilter; import kieker.analysis.plugin.filter.forward.StringBufferFilter; import kieker.analysis.plugin.filter.select.TimestampFilter; import kieker.analysis.plugin.filter.select.TraceIdFilter; import kieker.analysis.plugin.reader.filesystem.FSReader; import kieker.common.configuration.Configuration; import kieker.common.logging.Log; import kieker.common.logging.LogFactory; import kieker.common.util.filesystem.FSUtil; import kieker.tools.AbstractCommandLineTool; import kieker.tools.traceAnalysis.filter.AbstractGraphProducingFilter; import kieker.tools.traceAnalysis.filter.AbstractMessageTraceProcessingFilter; import kieker.tools.traceAnalysis.filter.AbstractTraceAnalysisFilter; import kieker.tools.traceAnalysis.filter.AbstractTraceProcessingFilter; import kieker.tools.traceAnalysis.filter.IGraphOutputtingFilter; import kieker.tools.traceAnalysis.filter.executionRecordTransformation.ExecutionRecordTransformationFilter; import kieker.tools.traceAnalysis.filter.flow.EventRecordTraceCounter; import kieker.tools.traceAnalysis.filter.flow.TraceEventRecords2ExecutionAndMessageTraceFilter; import kieker.tools.traceAnalysis.filter.systemModel.SystemModel2FileFilter; import kieker.tools.traceAnalysis.filter.traceFilter.TraceEquivalenceClassFilter; import kieker.tools.traceAnalysis.filter.traceFilter.TraceEquivalenceClassFilter.TraceEquivalenceClassModes; import kieker.tools.traceAnalysis.filter.traceReconstruction.TraceReconstructionFilter; import kieker.tools.traceAnalysis.filter.traceWriter.ExecutionTraceWriterFilter; import kieker.tools.traceAnalysis.filter.traceWriter.InvalidExecutionTraceWriterFilter; import kieker.tools.traceAnalysis.filter.traceWriter.MessageTraceWriterFilter; import kieker.tools.traceAnalysis.filter.visualization.AbstractGraphFilter; import kieker.tools.traceAnalysis.filter.visualization.GraphWriterPlugin; import kieker.tools.traceAnalysis.filter.visualization.callTree.AbstractAggregatedCallTreeFilter; import kieker.tools.traceAnalysis.filter.visualization.callTree.AggregatedAllocationComponentOperationCallTreeFilter; import kieker.tools.traceAnalysis.filter.visualization.callTree.AggregatedAssemblyComponentOperationCallTreeFilter; import kieker.tools.traceAnalysis.filter.visualization.callTree.TraceCallTreeFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.AbstractDependencyGraphFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.ComponentDependencyGraphAllocationFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.ComponentDependencyGraphAssemblyFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.ContainerDependencyGraphFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.OperationDependencyGraphAllocationFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.OperationDependencyGraphAssemblyFilter; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.ResponseTimeColorNodeDecorator; import kieker.tools.traceAnalysis.filter.visualization.dependencyGraph.ResponseTimeNodeDecorator; import kieker.tools.traceAnalysis.filter.visualization.descriptions.DescriptionDecoratorFilter; import kieker.tools.traceAnalysis.filter.visualization.sequenceDiagram.SequenceDiagramFilter; import kieker.tools.traceAnalysis.filter.visualization.traceColoring.TraceColoringFilter; import kieker.tools.traceAnalysis.repository.DescriptionRepository; import kieker.tools.traceAnalysis.repository.TraceColorRepository; import kieker.tools.traceAnalysis.systemModel.ExecutionTrace; import kieker.tools.traceAnalysis.systemModel.repository.SystemModelRepository; import kieker.tools.util.CLIHelpFormatter; import kieker.tools.util.LoggingTimestampConverter; /** * This is the main class to start the Kieker TraceAnalysisTool - the model synthesis and analysis tool to process the monitoring data that comes from the * instrumented system, or from a file that contains Kieker monitoring data. The Kieker TraceAnalysisTool can produce output such as sequence diagrams, dependency * graphs on demand. Alternatively it can be used continuously for online performance analysis, anomaly detection or live visualization of system behavior. * * @author Andre van Hoorn, Matthias Rohr, Nils Christian Ehmke * * @since 0.95a */ public final class TraceAnalysisTool extends AbstractCommandLineTool { // NOPMD (long class) public static final String DATE_FORMAT_PATTERN_CMD_USAGE_HELP = Constants.DATE_FORMAT_PATTERN.replaceAll("'", "") + " | timestamp"; // only for usage info private static final Log LOG = LogFactory.getLog(TraceAnalysisTool.class); private static final String ENCODING = "UTF-8"; private final AnalysisController analysisController = new AnalysisController(); private String[] inputDirs; private String outputDir; private String outputFnPrefix; private Set<Long> selectedTraces; // null means select all private boolean invertTraceIdFilter; private boolean ignoreAssumedCalls; private boolean shortLabels = true; private boolean includeSelfLoops; // false private boolean ignoreInvalidTraces; // false private boolean repairEventBasedTraces; // false private int maxTraceDurationMillis = 10 * 60 * 1000; // 10 minutes default private long ignoreExecutionsBeforeTimestamp = Long .parseLong(TimestampFilter.CONFIG_PROPERTY_VALUE_MIN_TIMESTAMP); private long ignoreExecutionsAfterTimestamp = Long .parseLong(TimestampFilter.CONFIG_PROPERTY_VALUE_MAX_TIMESTAMP); private CommandLine cmdl; private TraceAnalysisTool(final boolean useSystemExit) { super(useSystemExit); } public static void main(final String[] args) { TraceAnalysisTool.mainHelper(args, true); } public static void mainHelper(final String[] args, final boolean useSystemExit) { new TraceAnalysisTool(useSystemExit).start(args); } @Override @SuppressWarnings("unchecked") protected void addAdditionalOptions(final Options options) { // Remember the inherited options for the help formatter final List<Option> inheritedOptions = new ArrayList<Option>(); inheritedOptions.addAll(options.getOptions()); for (final Object option : Constants.CMDL_OPTIONS.getOptions()) { options.addOption((Option) option); } for (final Option option : inheritedOptions) { if (!Constants.SORTED_OPTION_LIST.contains(option)) { Constants.SORTED_OPTION_LIST.add(option); } } } @Override protected boolean readPropertiesFromCommandLine(final CommandLine commandLine) { this.cmdl = commandLine; return (this.initFromArgs(commandLine) && this.assertOutputDirExists() && this.assertInputDirsExistsAndAreMonitoringLogs()); } @Override protected boolean performTask() { this.dumpConfiguration(); return this.dispatchTasks(); } @Override protected HelpFormatter getHelpFormatter() { final HelpFormatter helpFormatter = new CLIHelpFormatter(); helpFormatter.setOptionComparator(new OptionComparator()); return helpFormatter; } /** * This method uses the (already parsed and stored) command line arguments to initialize the tool. * * @param commandLine * * @return true if and only if the tool has been initialized correctly. */ private boolean initFromArgs(final CommandLine commandLine) { this.inputDirs = commandLine.getOptionValues(Constants.CMD_OPT_NAME_INPUTDIRS); this.outputDir = commandLine.getOptionValue(Constants.CMD_OPT_NAME_OUTPUTDIR); this.outputFnPrefix = this.cmdl.getOptionValue(Constants.CMD_OPT_NAME_OUTPUTFNPREFIX, ""); if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_SELECTTRACES) && this.cmdl.hasOption(Constants.CMD_OPT_NAME_FILTERTRACES)) { LOG.error("Trace Id selection and filtering are mutually exclusive"); return false; } if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_SELECTTRACES) || this.cmdl.hasOption(Constants.CMD_OPT_NAME_FILTERTRACES)) { // Parse list of trace Ids this.invertTraceIdFilter = this.cmdl.hasOption(Constants.CMD_OPT_NAME_FILTERTRACES); final String[] traceIdList = this.cmdl .getOptionValues(this.invertTraceIdFilter ? Constants.CMD_OPT_NAME_FILTERTRACES // NOCS (Short if operator) : Constants.CMD_OPT_NAME_SELECTTRACES); this.selectedTraces = new TreeSet<Long>(); final int numSelectedTraces = traceIdList.length; try { for (final String idStr : traceIdList) { this.selectedTraces.add(Long.valueOf(idStr)); } LOG.info(numSelectedTraces + " trace" + (numSelectedTraces > 1 ? "s" : "") + (this.invertTraceIdFilter ? " filtered" : " selected")); // NOCS } catch (final Exception e) { // NOPMD NOCS (IllegalCatchCheck) LOG.error("Failed to parse list of trace IDs: " + Arrays.toString(traceIdList), e); return false; } } this.shortLabels = commandLine.hasOption(Constants.CMD_OPT_NAME_SHORTLABELS); this.includeSelfLoops = commandLine.hasOption(Constants.CMD_OPT_NAME_INCLUDESELFLOOPS); this.ignoreInvalidTraces = commandLine.hasOption(Constants.CMD_OPT_NAME_IGNOREINVALIDTRACES); this.ignoreAssumedCalls = commandLine.hasOption(Constants.CMD_OPT_NAME_IGNORE_ASSUMED); this.repairEventBasedTraces = commandLine.hasOption(Constants.CMD_OPT_NAME_REPAIR_EVENT_BASED_TRACES); final String maxTraceDurationStr = commandLine.getOptionValue(Constants.CMD_OPT_NAME_MAXTRACEDURATION, Integer.toString(this.maxTraceDurationMillis)); try { this.maxTraceDurationMillis = Integer.parseInt(maxTraceDurationStr); } catch (final NumberFormatException exc) { LOG.error("Failed to parse int value of property " + Constants.CMD_OPT_NAME_MAXTRACEDURATION + " (must be an integer):" + maxTraceDurationStr, exc); return false; } final DateFormat dateFormat = new SimpleDateFormat(Constants.DATE_FORMAT_PATTERN, Locale.US); dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); try { final String ignoreRecordsBeforeTimestampString = commandLine .getOptionValue(Constants.CMD_OPT_NAME_IGNOREEXECUTIONSBEFOREDATE, null); final String ignoreRecordsAfterTimestampString = commandLine .getOptionValue(Constants.CMD_OPT_NAME_IGNOREEXECUTIONSAFTERDATE, null); if (ignoreRecordsBeforeTimestampString != null) { long ignoreExecutionsBeforeTimestampTemp; try { ignoreExecutionsBeforeTimestampTemp = Long.parseLong(ignoreRecordsBeforeTimestampString); LOG.info("Ignoring records before " + ignoreExecutionsBeforeTimestampTemp); } catch (final NumberFormatException ex) { final Date ignoreBeforeDate = dateFormat.parse(ignoreRecordsBeforeTimestampString); ignoreExecutionsBeforeTimestampTemp = ignoreBeforeDate.getTime() * (1000 * 1000); LOG.info("Ignoring records before " + dateFormat.format(ignoreBeforeDate) + " (" + ignoreExecutionsBeforeTimestampTemp + ")"); } this.ignoreExecutionsBeforeTimestamp = ignoreExecutionsBeforeTimestampTemp; } if (ignoreRecordsAfterTimestampString != null) { long ignoreExecutionsAfterTimestampTemp; try { ignoreExecutionsAfterTimestampTemp = Long.parseLong(ignoreRecordsAfterTimestampString); LOG.info("Ignoring records after " + ignoreExecutionsAfterTimestampTemp); } catch (final NumberFormatException ex) { final Date ignoreAfterDate = dateFormat.parse(ignoreRecordsAfterTimestampString); ignoreExecutionsAfterTimestampTemp = ignoreAfterDate.getTime() * (1000 * 1000); LOG.info("Ignoring records after " + dateFormat.format(ignoreAfterDate) + " (" + ignoreExecutionsAfterTimestampTemp + ")"); } this.ignoreExecutionsAfterTimestamp = ignoreExecutionsAfterTimestampTemp; } } catch (final java.text.ParseException ex) { final String errorMsg = "Error parsing date/time string. Please use the following pattern: " + DATE_FORMAT_PATTERN_CMD_USAGE_HELP; LOG.error(errorMsg, ex); return false; } return true; } /** * Returns if the specified output directory {@link #outputDir} exists. If * the directory does not exist, an error message is printed to stderr. * * @return true if {@link #outputDir} is exists and is a directory; false * otherwise */ private boolean assertOutputDirExists() { if ((this.outputDir == null) || this.outputDir.isEmpty()) { LOG.error("No output directory configured"); return false; } final File outputDirFile = new File(this.outputDir); try { if (!outputDirFile.exists()) { LOG.error( "The specified output directory '" + outputDirFile.getCanonicalPath() + "' does not exist"); return false; } if (!outputDirFile.isDirectory()) { LOG.error("The specified output directory '" + outputDirFile.getCanonicalPath() + "' is not a directory"); return false; } } catch (final IOException e) { // thrown by File.getCanonicalPath() LOG.error("Error resolving name of output directory: '" + this.outputDir + "'"); } return true; } private static void addDecorators(final String[] decoratorNames, final AbstractDependencyGraphFilter<?> plugin) { if (decoratorNames == null) { return; } final List<String> decoratorList = Arrays.asList(decoratorNames); final Iterator<String> decoratorIterator = decoratorList.iterator(); while (decoratorIterator.hasNext()) { final String currentDecoratorStr = decoratorIterator.next(); if (Constants.RESPONSE_TIME_DECORATOR_FLAG_NS.equals(currentDecoratorStr)) { plugin.addDecorator(new ResponseTimeNodeDecorator(TimeUnit.NANOSECONDS)); continue; } else if (Constants.RESPONSE_TIME_DECORATOR_FLAG_US.equals(currentDecoratorStr)) { plugin.addDecorator(new ResponseTimeNodeDecorator(TimeUnit.MICROSECONDS)); continue; } else if (Constants.RESPONSE_TIME_DECORATOR_FLAG_MS.equals(currentDecoratorStr)) { plugin.addDecorator(new ResponseTimeNodeDecorator(TimeUnit.MILLISECONDS)); continue; } else if (Constants.RESPONSE_TIME_DECORATOR_FLAG_S.equals(currentDecoratorStr)) { plugin.addDecorator(new ResponseTimeNodeDecorator(TimeUnit.SECONDS)); continue; } else if (Constants.RESPONSE_TIME_COLORING_DECORATOR_FLAG.equals(currentDecoratorStr)) { // if decorator is responseColoring, next value should be the threshold final String thresholdStringStr = decoratorIterator.next(); try { final int threshold = Integer.parseInt(thresholdStringStr); plugin.addDecorator(new ResponseTimeColorNodeDecorator(threshold)); } catch (final NumberFormatException exc) { System.err.println( "\nFailed to parse int value of property " + "threshold(ms) : " + thresholdStringStr); // NOPMD (System.out) } } else { LOG.warn("Unknown decoration name '" + currentDecoratorStr + "'."); return; } } } /** * Returns if the specified input directories {@link #inputDirs} exist and that * each one is a monitoring log. If this is not the case for one of the directories, * an error message is printed to stderr. * * @return true if {@link #outputDir} is exists and is a directory; false * otherwise */ private boolean assertInputDirsExistsAndAreMonitoringLogs() { if (this.inputDirs == null) { LOG.error("No input directories configured"); return false; } for (final String inputDir : this.inputDirs) { final File inputDirFile = new File(inputDir); try { if (!inputDirFile.exists()) { LOG.error("The specified input directory '" + inputDirFile.getCanonicalPath() + "' does not exist"); return false; } if (!inputDirFile.isDirectory() && !inputDir.endsWith(FSUtil.ZIP_FILE_EXTENSION)) { LOG.error("The specified input directory '" + inputDirFile.getCanonicalPath() + "' is neither a directory nor a zip file"); return false; } // check whether inputDirFile contains a (kieker|tpmon).map file; the latter for legacy reasons if (inputDirFile.isDirectory()) { // only check for dirs final File[] mapFiles = { new File(inputDir + File.separatorChar + FSUtil.MAP_FILENAME), new File(inputDir + File.separatorChar + FSUtil.LEGACY_MAP_FILENAME), }; boolean mapFileExists = false; for (final File potentialMapFile : mapFiles) { if (potentialMapFile.isFile()) { mapFileExists = true; break; } } if (!mapFileExists) { LOG.error("The specified input directory '" + inputDirFile.getCanonicalPath() + "' is not a kieker log directory"); return false; } } } catch (final IOException e) { // thrown by File.getCanonicalPath() LOG.error("Error resolving name of input directory: '" + inputDir + "'"); } } return true; } /** * * @return false iff an error occurred */ private boolean dispatchTasks() { boolean retVal = true; int numRequestedTasks = 0; final SystemModelRepository systemEntityFactory = new SystemModelRepository(new Configuration(), this.analysisController); TraceReconstructionFilter mtReconstrFilter = null; EventRecordTraceCounter eventRecordTraceCounter = null; EventRecordTraceReconstructionFilter eventTraceReconstructionFilter = null; TraceEventRecords2ExecutionAndMessageTraceFilter traceEvents2ExecutionAndMessageTraceFilter = null; try { FSReader reader; { // NOCS (NestedBlock) final Configuration conf = new Configuration(null); conf.setProperty(FSReader.CONFIG_PROPERTY_NAME_INPUTDIRS, Configuration.toProperty(this.inputDirs)); conf.setProperty(FSReader.CONFIG_PROPERTY_NAME_IGNORE_UNKNOWN_RECORD_TYPES, Boolean.TRUE.toString()); reader = new FSReader(conf, this.analysisController); } // Unify Strings final StringBufferFilter stringBufferFilter = new StringBufferFilter(new Configuration(), this.analysisController); this.analysisController.connect(reader, FSReader.OUTPUT_PORT_NAME_RECORDS, stringBufferFilter, StringBufferFilter.INPUT_PORT_NAME_EVENTS); // This map can be used within the constructor for all following plugins which use the repository with the name defined in the // AbstractTraceAnalysisPlugin. final TimestampFilter timestampFilter; { // NOCS (nested block) // Create the timestamp filter and connect to the reader's output port final Configuration configTimestampFilter = new Configuration(); configTimestampFilter.setProperty(TimestampFilter.CONFIG_PROPERTY_NAME_IGNORE_BEFORE_TIMESTAMP, Long.toString(this.ignoreExecutionsBeforeTimestamp)); configTimestampFilter.setProperty(TimestampFilter.CONFIG_PROPERTY_NAME_IGNORE_AFTER_TIMESTAMP, Long.toString(this.ignoreExecutionsAfterTimestamp)); timestampFilter = new TimestampFilter(configTimestampFilter, this.analysisController); this.analysisController.connect(stringBufferFilter, StringBufferFilter.OUTPUT_PORT_NAME_RELAYED_EVENTS, timestampFilter, TimestampFilter.INPUT_PORT_NAME_EXECUTION); this.analysisController.connect(stringBufferFilter, StringBufferFilter.OUTPUT_PORT_NAME_RELAYED_EVENTS, timestampFilter, TimestampFilter.INPUT_PORT_NAME_FLOW); } final TraceIdFilter traceIdFilter; { // NOCS (nested block) // Create the trace ID filter and connect to the timestamp filter's output port final Configuration configTraceIdFilterFlow = new Configuration(); if (this.selectedTraces == null) { configTraceIdFilterFlow.setProperty(TraceIdFilter.CONFIG_PROPERTY_NAME_SELECT_ALL_TRACES, Boolean.TRUE.toString()); } else { configTraceIdFilterFlow.setProperty(TraceIdFilter.CONFIG_PROPERTY_NAME_SELECT_ALL_TRACES, Boolean.FALSE.toString()); configTraceIdFilterFlow.setProperty(TraceIdFilter.CONFIG_PROPERTY_NAME_SELECTED_TRACES, Configuration .toProperty(this.selectedTraces.toArray(new Long[this.selectedTraces.size()]))); } traceIdFilter = new TraceIdFilter(configTraceIdFilterFlow, this.analysisController); this.analysisController.connect(timestampFilter, TimestampFilter.OUTPUT_PORT_NAME_WITHIN_PERIOD, traceIdFilter, TraceIdFilter.INPUT_PORT_NAME_COMBINED); } final ExecutionRecordTransformationFilter execRecTransformer; { // NOCS (nested block) // Create the execution record transformation filter and connect to the trace ID filter's output port final Configuration execRecTransformerConfig = new Configuration(); execRecTransformerConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.EXEC_TRACE_RECONSTR_COMPONENT_NAME); execRecTransformer = new ExecutionRecordTransformationFilter(execRecTransformerConfig, this.analysisController); if (this.invertTraceIdFilter) { this.analysisController.connect(traceIdFilter, TraceIdFilter.OUTPUT_PORT_NAME_MISMATCH, execRecTransformer, ExecutionRecordTransformationFilter.INPUT_PORT_NAME_RECORDS); } else { this.analysisController.connect(traceIdFilter, TraceIdFilter.OUTPUT_PORT_NAME_MATCH, execRecTransformer, ExecutionRecordTransformationFilter.INPUT_PORT_NAME_RECORDS); } this.analysisController.connect(execRecTransformer, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); } { // NOCS (nested block) // Create the trace reconstruction filter and connect to the record transformation filter's output port final Configuration mtReconstrFilterConfig = new Configuration(); mtReconstrFilterConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.TRACERECONSTR_COMPONENT_NAME); mtReconstrFilterConfig.setProperty(TraceReconstructionFilter.CONFIG_PROPERTY_NAME_TIMEUNIT, TimeUnit.MILLISECONDS.name()); mtReconstrFilterConfig.setProperty( TraceReconstructionFilter.CONFIG_PROPERTY_NAME_MAX_TRACE_DURATION, Integer.toString(this.maxTraceDurationMillis)); mtReconstrFilterConfig.setProperty( TraceReconstructionFilter.CONFIG_PROPERTY_NAME_IGNORE_INVALID_TRACES, Boolean.toString(this.ignoreInvalidTraces)); mtReconstrFilter = new TraceReconstructionFilter(mtReconstrFilterConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); this.analysisController.connect(execRecTransformer, ExecutionRecordTransformationFilter.OUTPUT_PORT_NAME_EXECUTIONS, mtReconstrFilter, TraceReconstructionFilter.INPUT_PORT_NAME_EXECUTIONS); } { // NOCS (nested block) // Create the event record trace generation filter and connect to the trace ID filter's output port final Configuration configurationEventRecordTraceGenerationFilter = new Configuration(); configurationEventRecordTraceGenerationFilter.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.EVENTRECORDTRACERECONSTR_COMPONENT_NAME); configurationEventRecordTraceGenerationFilter.setProperty( EventRecordTraceReconstructionFilter.CONFIG_PROPERTY_NAME_TIMEUNIT, TimeUnit.MILLISECONDS.name()); configurationEventRecordTraceGenerationFilter.setProperty( EventRecordTraceReconstructionFilter.CONFIG_PROPERTY_NAME_MAX_TRACE_DURATION, Long.toString(this.maxTraceDurationMillis)); configurationEventRecordTraceGenerationFilter.setProperty( EventRecordTraceReconstructionFilter.CONFIG_PROPERTY_NAME_REPAIR_EVENT_BASED_TRACES, Boolean.toString(this.repairEventBasedTraces)); eventTraceReconstructionFilter = new EventRecordTraceReconstructionFilter( configurationEventRecordTraceGenerationFilter, this.analysisController); if (this.invertTraceIdFilter) { this.analysisController.connect(traceIdFilter, TraceIdFilter.OUTPUT_PORT_NAME_MISMATCH, eventTraceReconstructionFilter, EventRecordTraceReconstructionFilter.INPUT_PORT_NAME_TRACE_RECORDS); } else { this.analysisController.connect(traceIdFilter, TraceIdFilter.OUTPUT_PORT_NAME_MATCH, eventTraceReconstructionFilter, EventRecordTraceReconstructionFilter.INPUT_PORT_NAME_TRACE_RECORDS); } } { // NOCS (nested block) // Create the counter for valid/invalid event record traces final Configuration configurationEventRecordTraceCounter = new Configuration(); configurationEventRecordTraceCounter.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.EXECEVENTRACESFROMEVENTTRACES_COMPONENT_NAME); configurationEventRecordTraceCounter.setProperty( EventRecordTraceCounter.CONFIG_PROPERTY_NAME_LOG_INVALID, Boolean.toString(!this.ignoreInvalidTraces)); eventRecordTraceCounter = new EventRecordTraceCounter(configurationEventRecordTraceCounter, this.analysisController); this.analysisController.connect(eventTraceReconstructionFilter, EventRecordTraceReconstructionFilter.OUTPUT_PORT_NAME_TRACE_VALID, eventRecordTraceCounter, EventRecordTraceCounter.INPUT_PORT_NAME_VALID); this.analysisController.connect(eventTraceReconstructionFilter, EventRecordTraceReconstructionFilter.OUTPUT_PORT_NAME_TRACE_INVALID, eventRecordTraceCounter, EventRecordTraceCounter.INPUT_PORT_NAME_INVALID); } { // NOCS (nested block) // Create the event trace to execution/message trace transformation filter and connect its input to the event record trace generation filter's output // port final Configuration configurationEventTrace2ExecutionTraceFilter = new Configuration(); configurationEventTrace2ExecutionTraceFilter.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.EXECTRACESFROMEVENTTRACES_COMPONENT_NAME); configurationEventTrace2ExecutionTraceFilter.setProperty( TraceEventRecords2ExecutionAndMessageTraceFilter.CONFIG_IGNORE_ASSUMED, Boolean.toString(this.ignoreAssumedCalls)); // EventTrace2ExecutionTraceFilter has no configuration properties traceEvents2ExecutionAndMessageTraceFilter = new TraceEventRecords2ExecutionAndMessageTraceFilter( configurationEventTrace2ExecutionTraceFilter, this.analysisController); this.analysisController.connect(eventTraceReconstructionFilter, EventRecordTraceReconstructionFilter.OUTPUT_PORT_NAME_TRACE_VALID, traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.INPUT_PORT_NAME_EVENT_TRACE); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); } final List<AbstractTraceProcessingFilter> allTraceProcessingComponents = new ArrayList<AbstractTraceProcessingFilter>(); final List<AbstractGraphProducingFilter<?>> allGraphProducers = new ArrayList<AbstractGraphProducingFilter<?>>(); final Configuration traceAllocationEquivClassFilterConfig = new Configuration(); traceAllocationEquivClassFilterConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.TRACEALLOCATIONEQUIVCLASS_COMPONENT_NAME); traceAllocationEquivClassFilterConfig.setProperty( TraceEquivalenceClassFilter.CONFIG_PROPERTY_NAME_EQUIVALENCE_MODE, TraceEquivalenceClassModes.ALLOCATION.toString()); TraceEquivalenceClassFilter traceAllocationEquivClassFilter = null; // must not be instantiate it here, due to side-effects in the constructor if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_ALLOCATIONEQUIVCLASSREPORT)) { /** * Currently, this filter is only used to print an equivalence * report. That's why we only activate it in case this options * is requested. */ traceAllocationEquivClassFilter = new TraceEquivalenceClassFilter( traceAllocationEquivClassFilterConfig, this.analysisController); this.analysisController.connect(traceAllocationEquivClassFilter, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_EXECUTION_TRACE, traceAllocationEquivClassFilter, TraceEquivalenceClassFilter.INPUT_PORT_NAME_EXECUTION_TRACE); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_EXECUTION_TRACE, traceAllocationEquivClassFilter, TraceEquivalenceClassFilter.INPUT_PORT_NAME_EXECUTION_TRACE); allTraceProcessingComponents.add(traceAllocationEquivClassFilter); } final Configuration traceAssemblyEquivClassFilterConfig = new Configuration(); traceAssemblyEquivClassFilterConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.TRACEASSEMBLYEQUIVCLASS_COMPONENT_NAME); traceAssemblyEquivClassFilterConfig.setProperty( TraceEquivalenceClassFilter.CONFIG_PROPERTY_NAME_EQUIVALENCE_MODE, TraceEquivalenceClassModes.ASSEMBLY.toString()); TraceEquivalenceClassFilter traceAssemblyEquivClassFilter = null; // must not be instantiate it here, due to side-effects in the constructor if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_ASSEMBLYEQUIVCLASSREPORT)) { /** * Currently, this filter is only used to print an equivalence * report. That's why we only activate it in case this options * is requested. */ traceAssemblyEquivClassFilter = new TraceEquivalenceClassFilter(traceAssemblyEquivClassFilterConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_EXECUTION_TRACE, traceAssemblyEquivClassFilter, TraceEquivalenceClassFilter.INPUT_PORT_NAME_EXECUTION_TRACE); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_EXECUTION_TRACE, traceAssemblyEquivClassFilter, TraceEquivalenceClassFilter.INPUT_PORT_NAME_EXECUTION_TRACE); this.analysisController.connect(traceAssemblyEquivClassFilter, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(traceAssemblyEquivClassFilter); } // fill list of msgTraceProcessingComponents: MessageTraceWriterFilter componentPrintMsgTrace = null; if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PRINTMSGTRACES)) { numRequestedTasks++; final Configuration componentPrintMsgTraceConfig = new Configuration(); componentPrintMsgTraceConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PRINTMSGTRACE_COMPONENT_NAME); componentPrintMsgTraceConfig.setProperty(MessageTraceWriterFilter.CONFIG_PROPERTY_NAME_OUTPUT_FN, new File(this.outputDir + File.separator + this.outputFnPrefix + Constants.MESSAGE_TRACES_FN_PREFIX + ".txt").getCanonicalPath()); componentPrintMsgTrace = new MessageTraceWriterFilter(componentPrintMsgTraceConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPrintMsgTrace, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPrintMsgTrace, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPrintMsgTrace, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPrintMsgTrace); } ExecutionTraceWriterFilter componentPrintExecTrace = null; if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PRINTEXECTRACES)) { numRequestedTasks++; final Configuration componentPrintExecTraceConfig = new Configuration(); componentPrintExecTraceConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PRINTEXECTRACE_COMPONENT_NAME); componentPrintExecTraceConfig.setProperty(ExecutionTraceWriterFilter.CONFIG_PROPERTY_NAME_OUTPUT_FN, new File(this.outputDir + File.separator + this.outputFnPrefix + Constants.EXECUTION_TRACES_FN_PREFIX + ".txt").getCanonicalPath()); componentPrintExecTrace = new ExecutionTraceWriterFilter(componentPrintExecTraceConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_EXECUTION_TRACE, componentPrintExecTrace, ExecutionTraceWriterFilter.INPUT_PORT_NAME_EXECUTION_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_EXECUTION_TRACE, componentPrintExecTrace, ExecutionTraceWriterFilter.INPUT_PORT_NAME_EXECUTION_TRACES); this.analysisController.connect(componentPrintExecTrace, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPrintExecTrace); } InvalidExecutionTraceWriterFilter componentPrintInvalidTrace = null; if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PRINTINVALIDEXECTRACES)) { numRequestedTasks++; final Configuration componentPrintInvalidTraceConfig = new Configuration(); componentPrintInvalidTraceConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PRINTINVALIDEXECTRACE_COMPONENT_NAME); componentPrintInvalidTraceConfig.setProperty( InvalidExecutionTraceWriterFilter.CONFIG_PROPERTY_NAME_OUTPUT_FN, new File(this.outputDir + File.separator + this.outputFnPrefix + Constants.INVALID_TRACES_FN_PREFIX + ".txt").getCanonicalPath()); componentPrintInvalidTrace = new InvalidExecutionTraceWriterFilter(componentPrintInvalidTraceConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_INVALID_EXECUTION_TRACE, componentPrintInvalidTrace, InvalidExecutionTraceWriterFilter.INPUT_PORT_NAME_INVALID_EXECUTION_TRACES); this.analysisController.connect(componentPrintInvalidTrace, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_INVALID_EXECUTION_TRACE, componentPrintInvalidTrace, InvalidExecutionTraceWriterFilter.INPUT_PORT_NAME_INVALID_EXECUTION_TRACES); allTraceProcessingComponents.add(componentPrintInvalidTrace); } SequenceDiagramFilter componentPlotAllocationSeqDiagr = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONSEQDS)) { numRequestedTasks++; final Configuration componentPlotAllocationSeqDiagrConfig = new Configuration(); componentPlotAllocationSeqDiagrConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PLOTALLOCATIONSEQDIAGR_COMPONENT_NAME); componentPlotAllocationSeqDiagrConfig.setProperty( SequenceDiagramFilter.CONFIG_PROPERTY_NAME_OUTPUT_FN_BASE, this.outputDir + File.separator + this.outputFnPrefix + Constants.ALLOCATION_SEQUENCE_DIAGRAM_FN_PREFIX); componentPlotAllocationSeqDiagrConfig.setProperty( SequenceDiagramFilter.CONFIG_PROPERTY_NAME_OUTPUT_SDMODE, SequenceDiagramFilter.SDModes.ALLOCATION.toString()); componentPlotAllocationSeqDiagrConfig.setProperty( SequenceDiagramFilter.CONFIG_PROPERTY_NAME_OUTPUT_SHORTLABES, Boolean.toString(this.shortLabels)); componentPlotAllocationSeqDiagr = new SequenceDiagramFilter(componentPlotAllocationSeqDiagrConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAllocationSeqDiagr, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAllocationSeqDiagr, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAllocationSeqDiagr, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAllocationSeqDiagr); } SequenceDiagramFilter componentPlotAssemblySeqDiagr = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYSEQDS)) { numRequestedTasks++; final Configuration componentPlotAssemblySeqDiagrConfig = new Configuration(); componentPlotAssemblySeqDiagrConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PLOTASSEMBLYSEQDIAGR_COMPONENT_NAME); componentPlotAssemblySeqDiagrConfig.setProperty( SequenceDiagramFilter.CONFIG_PROPERTY_NAME_OUTPUT_FN_BASE, this.outputDir + File.separator + this.outputFnPrefix + Constants.ASSEMBLY_SEQUENCE_DIAGRAM_FN_PREFIX); componentPlotAssemblySeqDiagrConfig.setProperty( SequenceDiagramFilter.CONFIG_PROPERTY_NAME_OUTPUT_SDMODE, SequenceDiagramFilter.SDModes.ASSEMBLY.toString()); componentPlotAssemblySeqDiagrConfig.setProperty( SequenceDiagramFilter.CONFIG_PROPERTY_NAME_OUTPUT_SHORTLABES, Boolean.toString(this.shortLabels)); componentPlotAssemblySeqDiagr = new SequenceDiagramFilter(componentPlotAssemblySeqDiagrConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblySeqDiagr, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblySeqDiagr, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAssemblySeqDiagr, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAssemblySeqDiagr); } ComponentDependencyGraphAllocationFilter componentPlotAllocationComponentDepGraph = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONCOMPONENTDEPG)) { numRequestedTasks++; final Configuration configuration = new Configuration(); componentPlotAllocationComponentDepGraph = new ComponentDependencyGraphAllocationFilter( configuration, this.analysisController); final String[] nodeDecorations = this.cmdl .getOptionValues(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONCOMPONENTDEPG); TraceAnalysisTool.addDecorators(nodeDecorations, componentPlotAllocationComponentDepGraph); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAllocationComponentDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAllocationComponentDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAllocationComponentDepGraph, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAllocationComponentDepGraph); allGraphProducers.add(componentPlotAllocationComponentDepGraph); } ComponentDependencyGraphAssemblyFilter componentPlotAssemblyComponentDepGraph = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYCOMPONENTDEPG)) { numRequestedTasks++; final Configuration configuration = new Configuration(); componentPlotAssemblyComponentDepGraph = new ComponentDependencyGraphAssemblyFilter(configuration, this.analysisController); final String[] nodeDecorations = this.cmdl .getOptionValues(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYCOMPONENTDEPG); TraceAnalysisTool.addDecorators(nodeDecorations, componentPlotAssemblyComponentDepGraph); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblyComponentDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblyComponentDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAssemblyComponentDepGraph, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAssemblyComponentDepGraph); allGraphProducers.add(componentPlotAssemblyComponentDepGraph); } ContainerDependencyGraphFilter componentPlotContainerDepGraph = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTCONTAINERDEPG)) { numRequestedTasks++; final Configuration configuration = new Configuration(); componentPlotContainerDepGraph = new ContainerDependencyGraphFilter(configuration, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotContainerDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotContainerDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotContainerDepGraph, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotContainerDepGraph); allGraphProducers.add(componentPlotContainerDepGraph); } OperationDependencyGraphAllocationFilter componentPlotAllocationOperationDepGraph = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONOPERATIONDEPG)) { numRequestedTasks++; final Configuration configuration = new Configuration(); componentPlotAllocationOperationDepGraph = new OperationDependencyGraphAllocationFilter( configuration, this.analysisController); final String[] nodeDecorations = this.cmdl .getOptionValues(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONOPERATIONDEPG); TraceAnalysisTool.addDecorators(nodeDecorations, componentPlotAllocationOperationDepGraph); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAllocationOperationDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAllocationOperationDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAllocationOperationDepGraph, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAllocationOperationDepGraph); allGraphProducers.add(componentPlotAllocationOperationDepGraph); } OperationDependencyGraphAssemblyFilter componentPlotAssemblyOperationDepGraph = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYOPERATIONDEPG)) { numRequestedTasks++; final Configuration configuration = new Configuration(); componentPlotAssemblyOperationDepGraph = new OperationDependencyGraphAssemblyFilter(configuration, this.analysisController); final String[] nodeDecorations = this.cmdl .getOptionValues(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYOPERATIONDEPG); TraceAnalysisTool.addDecorators(nodeDecorations, componentPlotAssemblyOperationDepGraph); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblyOperationDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblyOperationDepGraph, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAssemblyOperationDepGraph, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAssemblyOperationDepGraph); allGraphProducers.add(componentPlotAssemblyOperationDepGraph); } TraceCallTreeFilter componentPlotTraceCallTrees = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTCALLTREES)) { numRequestedTasks++; final Configuration componentPlotTraceCallTreesConfig = new Configuration(); componentPlotTraceCallTreesConfig.setProperty( TraceCallTreeFilter.CONFIG_PROPERTY_NAME_OUTPUT_FILENAME, new File(this.outputDir + File.separator + this.outputFnPrefix + Constants.CALL_TREE_FN_PREFIX).getCanonicalPath()); componentPlotTraceCallTreesConfig.setProperty(TraceCallTreeFilter.CONFIG_PROPERTY_NAME_SHORT_LABELS, Boolean.toString(this.shortLabels)); componentPlotTraceCallTreesConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PLOTCALLTREE_COMPONENT_NAME); componentPlotTraceCallTrees = new TraceCallTreeFilter(componentPlotTraceCallTreesConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotTraceCallTrees, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotTraceCallTrees, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotTraceCallTrees, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotTraceCallTrees); } AggregatedAllocationComponentOperationCallTreeFilter componentPlotAggregatedCallTree = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTAGGREGATEDALLOCATIONCALLTREE)) { numRequestedTasks++; final Configuration componentPlotAggregatedCallTreeConfig = new Configuration(); componentPlotAggregatedCallTreeConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PLOTAGGREGATEDALLOCATIONCALLTREE_COMPONENT_NAME); componentPlotAggregatedCallTreeConfig.setProperty( AbstractAggregatedCallTreeFilter.CONFIG_PROPERTY_NAME_INCLUDE_WEIGHTS, Boolean.toString(true)); componentPlotAggregatedCallTreeConfig.setProperty( AbstractAggregatedCallTreeFilter.CONFIG_PROPERTY_NAME_SHORT_LABELS, Boolean.toString(this.shortLabels)); componentPlotAggregatedCallTreeConfig.setProperty( AbstractAggregatedCallTreeFilter.CONFIG_PROPERTY_NAME_OUTPUT_FILENAME, this.outputDir + File.separator + this.outputFnPrefix + Constants.AGGREGATED_ALLOCATION_CALL_TREE_FN_PREFIX + ".dot"); componentPlotAggregatedCallTree = new AggregatedAllocationComponentOperationCallTreeFilter( componentPlotAggregatedCallTreeConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAggregatedCallTree, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAggregatedCallTree, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAggregatedCallTree, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAggregatedCallTree); } AggregatedAssemblyComponentOperationCallTreeFilter componentPlotAssemblyCallTree = null; if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PLOTAGGREGATEDASSEMBLYCALLTREE)) { numRequestedTasks++; final Configuration componentPlotAssemblyCallTreeConfig = new Configuration(); componentPlotAssemblyCallTreeConfig.setProperty(AbstractAnalysisComponent.CONFIG_NAME, Constants.PLOTAGGREGATEDASSEMBLYCALLTREE_COMPONENT_NAME); componentPlotAssemblyCallTreeConfig.setProperty( AbstractAggregatedCallTreeFilter.CONFIG_PROPERTY_NAME_INCLUDE_WEIGHTS, Boolean.toString(true)); componentPlotAssemblyCallTreeConfig.setProperty( AbstractAggregatedCallTreeFilter.CONFIG_PROPERTY_NAME_SHORT_LABELS, Boolean.toString(this.shortLabels)); componentPlotAssemblyCallTreeConfig.setProperty( AbstractAggregatedCallTreeFilter.CONFIG_PROPERTY_NAME_OUTPUT_FILENAME, this.outputDir + File.separator + this.outputFnPrefix + Constants.AGGREGATED_ASSEMBLY_CALL_TREE_FN_PREFIX + ".dot"); componentPlotAssemblyCallTree = new AggregatedAssemblyComponentOperationCallTreeFilter( componentPlotAssemblyCallTreeConfig, this.analysisController); this.analysisController.connect(mtReconstrFilter, TraceReconstructionFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblyCallTree, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(traceEvents2ExecutionAndMessageTraceFilter, TraceEventRecords2ExecutionAndMessageTraceFilter.OUTPUT_PORT_NAME_MESSAGE_TRACE, componentPlotAssemblyCallTree, AbstractMessageTraceProcessingFilter.INPUT_PORT_NAME_MESSAGE_TRACES); this.analysisController.connect(componentPlotAssemblyCallTree, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); allTraceProcessingComponents.add(componentPlotAssemblyCallTree); } if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_ALLOCATIONEQUIVCLASSREPORT)) { numRequestedTasks++; // the actual execution of the task is performed below } if (this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_PRINTSYSTEMMODEL)) { numRequestedTasks++; } // Attach graph processors to the graph producers this.attachGraphProcessors(allGraphProducers, this.analysisController, this.cmdl); if (numRequestedTasks == 0) { LOG.error("No task requested"); LOG.info("Use the option `--" + CMD_OPT_NAME_HELP_LONG + "` for usage information"); return false; } if (retVal) { final String systemEntitiesHtmlFn = this.outputDir + File.separator + this.outputFnPrefix + "system-entities.html"; final Configuration systemModel2FileFilterConfig = new Configuration(); systemModel2FileFilterConfig.setProperty(SystemModel2FileFilter.CONFIG_PROPERTY_NAME_HTML_OUTPUT_FN, systemEntitiesHtmlFn); final SystemModel2FileFilter systemModel2FileFilter = new SystemModel2FileFilter( systemModel2FileFilterConfig, this.analysisController); // note that this plugin is (currently) not connected to any other filters this.analysisController.connect(systemModel2FileFilter, AbstractTraceAnalysisFilter.REPOSITORY_PORT_NAME_SYSTEM_MODEL, systemEntityFactory); } int numErrorCount = 0; try { this.analysisController.run(); if (this.analysisController.getState() != AnalysisController.STATE.TERMINATED) { // Analysis did not terminate successfully retVal = false; // Error message referring to log will be printed later LOG.error("Analysis instance terminated in state other than" + AnalysisController.STATE.TERMINATED + ":" + this.analysisController.getState()); } } finally { for (final AbstractTraceProcessingFilter c : allTraceProcessingComponents) { numErrorCount += c.getErrorCount(); c.printStatusMessage(); } final String kaxOutputFn = this.outputDir + File.separator + this.outputFnPrefix + "traceAnalysis.kax"; final File kaxOutputFile = new File(kaxOutputFn); try { // NOCS (nested try) // Try to serialize analysis configuration to .kax file this.analysisController.saveToFile(kaxOutputFile); LOG.info("Saved analysis configuration to file '" + kaxOutputFile.getCanonicalPath() + "'"); } catch (final IOException ex) { LOG.error("Failed to save analysis configuration to file '" + kaxOutputFile.getCanonicalPath() + "'"); } } if (!this.ignoreInvalidTraces && (numErrorCount > 0)) { throw new Exception(numErrorCount + " errors occured in trace processing components"); } if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_ALLOCATIONEQUIVCLASSREPORT)) { retVal = this.writeTraceEquivalenceReport( this.outputDir + File.separator + this.outputFnPrefix + Constants.TRACE_ALLOCATION_EQUIV_CLASSES_FN_PREFIX + ".txt", traceAllocationEquivClassFilter); } if (retVal && this.cmdl.hasOption(Constants.CMD_OPT_NAME_TASK_ASSEMBLYEQUIVCLASSREPORT)) { retVal = this.writeTraceEquivalenceReport( this.outputDir + File.separator + this.outputFnPrefix + Constants.TRACE_ASSEMBLY_EQUIV_CLASSES_FN_PREFIX + ".txt", traceAssemblyEquivClassFilter); } } catch (final Exception ex) { // NOPMD NOCS (IllegalCatchCheck) LOG.error("An error occured", ex); retVal = false; } finally { if (numRequestedTasks > 0) { if (mtReconstrFilter != null) { mtReconstrFilter.printStatusMessage(); } if (eventRecordTraceCounter != null) { eventRecordTraceCounter.printStatusMessage(); } if (traceEvents2ExecutionAndMessageTraceFilter != null) { traceEvents2ExecutionAndMessageTraceFilter.printStatusMessage(); } } } return retVal; } /** * This method dumps the configuration on the screen. */ private void dumpConfiguration() { LOG.debug("#"); LOG.debug("# Configuration"); for (final Option o : Constants.SORTED_OPTION_LIST) { final String longOpt = o.getLongOpt(); String val = "<null>"; if (longOpt.equals(Constants.CMD_OPT_NAME_INPUTDIRS)) { val = Constants.stringArrToStringList(this.inputDirs); } else if (longOpt.equals(Constants.CMD_OPT_NAME_OUTPUTDIR)) { val = this.outputDir; } else if (longOpt.equals(Constants.CMD_OPT_NAME_OUTPUTFNPREFIX)) { val = this.outputFnPrefix; } else if (longOpt.equals(Constants.CMD_OPT_NAME_TASK_ALLOCATIONEQUIVCLASSREPORT) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_ASSEMBLYEQUIVCLASSREPORT) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONSEQDS) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYSEQDS) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONCOMPONENTDEPG) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYCOMPONENTDEPG) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTCONTAINERDEPG) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTALLOCATIONOPERATIONDEPG) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTASSEMBLYOPERATIONDEPG) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTAGGREGATEDALLOCATIONCALLTREE) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTAGGREGATEDASSEMBLYCALLTREE) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PLOTCALLTREES) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PRINTEXECTRACES) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PRINTINVALIDEXECTRACES) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PRINTMSGTRACES) || longOpt.equals(Constants.CMD_OPT_NAME_TASK_PRINTSYSTEMMODEL) || longOpt.equals(AbstractCommandLineTool.CMD_OPT_NAME_DEBUG_LONG) || longOpt.equals(AbstractCommandLineTool.CMD_OPT_NAME_VERBOSE_LONG) || longOpt.equals(AbstractCommandLineTool.CMD_OPT_NAME_HELP_LONG)) { val = this.cmdl.hasOption(longOpt) ? "true" : "false"; // NOCS } else if (longOpt.equals(Constants.CMD_OPT_NAME_SELECTTRACES)) { if (this.selectedTraces != null) { val = this.selectedTraces.toString(); } else { val = "<select all>"; } } else if (longOpt.equals(Constants.CMD_OPT_NAME_FILTERTRACES)) { if (this.selectedTraces != null) { val = this.selectedTraces.toString(); } else { val = "<filter none>"; } } else if (longOpt.equals(Constants.CMD_OPT_NAME_SHORTLABELS)) { val = this.shortLabels ? "true" : "false"; // NOCS } else if (longOpt.equals(Constants.CMD_OPT_NAME_INCLUDESELFLOOPS)) { val = this.includeSelfLoops ? "true" : "false"; // NOCS } else if (longOpt.equals(Constants.CMD_OPT_NAME_IGNORE_ASSUMED)) { val = this.ignoreAssumedCalls ? "true" : "false"; // NOCS } else if (longOpt.equals(Constants.CMD_OPT_NAME_IGNOREINVALIDTRACES)) { val = this.ignoreInvalidTraces ? "true" : "false"; // NOCS } else if (longOpt.equals(Constants.CMD_OPT_NAME_REPAIR_EVENT_BASED_TRACES)) { val = this.repairEventBasedTraces ? "true" : "false"; // NOCS } else if (longOpt.equals(Constants.CMD_OPT_NAME_MAXTRACEDURATION)) { val = this.maxTraceDurationMillis + " ms"; } else if (longOpt.equals(Constants.CMD_OPT_NAME_IGNOREEXECUTIONSBEFOREDATE)) { val = LoggingTimestampConverter .convertLoggingTimestampToUTCString(this.ignoreExecutionsBeforeTimestamp) + " (" + LoggingTimestampConverter.convertLoggingTimestampLocalTimeZoneString( this.ignoreExecutionsBeforeTimestamp) + ")"; } else if (longOpt.equals(Constants.CMD_OPT_NAME_IGNOREEXECUTIONSAFTERDATE)) { val = LoggingTimestampConverter .convertLoggingTimestampToUTCString(this.ignoreExecutionsAfterTimestamp) + " (" + LoggingTimestampConverter.convertLoggingTimestampLocalTimeZoneString( this.ignoreExecutionsAfterTimestamp) + ")"; } else if (Constants.CMD_OPT_NAME_TRACE_COLORING.equals(longOpt)) { val = this.cmdl.getOptionValue(Constants.CMD_OPT_NAME_TRACE_COLORING); if (val == null) { val = ""; } } else if (Constants.CMD_OPT_NAME_ADD_DESCRIPTIONS.equals(longOpt)) { val = this.cmdl.getOptionValue(Constants.CMD_OPT_NAME_ADD_DESCRIPTIONS); if (val == null) { val = ""; } } else { val = Arrays.toString(this.cmdl.getOptionValues(longOpt)); LOG.warn("Unformatted configuration output for option " + longOpt); } if (LOG.isDebugEnabled()) { LOG.debug("--" + longOpt + ": " + val); } } } /** * Attaches a graph writer plugin to the given plugin. * * @param plugin * The plugin which delivers the graph to write * @param producer * The producer which originally produced the graph * @param controller * The analysis controller to use for the connection of the plugins * @throws IllegalStateException * If the connection of the plugins is not possible at the moment * @throws AnalysisConfigurationException * If the plugins cannot be connected * * @param <P> * The type of the plugin. */ private <P extends AbstractPlugin & IGraphOutputtingFilter<?>> void attachGraphWriter(final P plugin, final AbstractGraphProducingFilter<?> producer, final AnalysisController controller) throws IllegalStateException, AnalysisConfigurationException { final Configuration configuration = new Configuration(); configuration.setProperty(GraphWriterPlugin.CONFIG_PROPERTY_NAME_OUTPUT_PATH_NAME, this.outputDir + File.separator + this.outputFnPrefix); configuration.setProperty(GraphWriterPlugin.CONFIG_PROPERTY_NAME_INCLUDE_WEIGHTS, String.valueOf(true)); configuration.setProperty(GraphWriterPlugin.CONFIG_PROPERTY_NAME_SHORTLABELS, String.valueOf(this.shortLabels)); configuration.setProperty(GraphWriterPlugin.CONFIG_PROPERTY_NAME_SELFLOOPS, String.valueOf(this.includeSelfLoops)); configuration.setProperty(AbstractAnalysisComponent.CONFIG_NAME, producer.getConfigurationName()); final GraphWriterPlugin graphWriter = new GraphWriterPlugin(configuration, controller); controller.connect(plugin, plugin.getGraphOutputPortName(), graphWriter, GraphWriterPlugin.INPUT_PORT_NAME_GRAPHS); } private static <P extends AbstractPlugin & IGraphOutputtingFilter<?>> void connectGraphFilters( final P predecessor, final AbstractGraphFilter<?, ?, ?, ?> filter, final AnalysisController controller) throws IllegalStateException, AnalysisConfigurationException { controller.connect(predecessor, predecessor.getGraphOutputPortName(), filter, filter.getGraphInputPortName()); } private static <P extends AbstractPlugin & IGraphOutputtingFilter<?>> TraceColoringFilter<?, ?> createTraceColoringFilter( final P predecessor, final String coloringFileName, final AnalysisController controller) throws IOException, IllegalStateException, AnalysisConfigurationException { final TraceColorRepository colorRepository = TraceColorRepository.createFromFile(coloringFileName, controller); @SuppressWarnings("rawtypes") final TraceColoringFilter<?, ?> coloringFilter = new TraceColoringFilter(new Configuration(), controller); TraceAnalysisTool.connectGraphFilters(predecessor, coloringFilter, controller); controller.connect(coloringFilter, TraceColoringFilter.COLOR_REPOSITORY_PORT_NAME, colorRepository); return coloringFilter; } private static <P extends AbstractPlugin & IGraphOutputtingFilter<?>> DescriptionDecoratorFilter<?, ?, ?> createDescriptionDecoratorFilter( final P predecessor, final String descriptionsFileName, final AnalysisController controller) throws IOException, IllegalStateException, AnalysisConfigurationException { final DescriptionRepository descriptionRepository = DescriptionRepository .createFromFile(descriptionsFileName, controller); @SuppressWarnings("rawtypes") final DescriptionDecoratorFilter<?, ?, ?> descriptionFilter = new DescriptionDecoratorFilter( new Configuration(), controller); TraceAnalysisTool.connectGraphFilters(predecessor, descriptionFilter, controller); controller.connect(descriptionFilter, DescriptionDecoratorFilter.DESCRIPTION_REPOSITORY_PORT_NAME, descriptionRepository); return descriptionFilter; } /** * Attaches graph processors and a writer to the given graph producers depending on the given * command line. * * @param graphProducers * The graph producers to connect processors to * @param controller * The analysis controller to use for the connection of the plugins * @param commandLine * The command line to determine the desired processors * * @throws IllegalStateException * If the connection of plugins is not possible at the moment * @throws AnalysisConfigurationException * If some plugins cannot be connected */ private void attachGraphProcessors(final List<AbstractGraphProducingFilter<?>> graphProducers, final AnalysisController controller, final CommandLine commandLine) throws IllegalStateException, AnalysisConfigurationException, IOException { for (final AbstractGraphProducingFilter<?> producer : graphProducers) { AbstractGraphFilter<?, ?, ?, ?> lastFilter = null; // Add a trace coloring filter, if necessary if (commandLine.hasOption(Constants.CMD_OPT_NAME_TRACE_COLORING)) { final String coloringFileName = commandLine.getOptionValue(Constants.CMD_OPT_NAME_TRACE_COLORING); lastFilter = TraceAnalysisTool.createTraceColoringFilter(producer, coloringFileName, controller); } // Add a description filter, if necessary if (commandLine.hasOption(Constants.CMD_OPT_NAME_ADD_DESCRIPTIONS)) { final String descriptionsFileName = commandLine .getOptionValue(Constants.CMD_OPT_NAME_ADD_DESCRIPTIONS); if (lastFilter != null) { lastFilter = TraceAnalysisTool.createDescriptionDecoratorFilter(lastFilter, descriptionsFileName, controller); } else { lastFilter = TraceAnalysisTool.createDescriptionDecoratorFilter(producer, descriptionsFileName, controller); } } if (lastFilter != null) { this.attachGraphWriter(lastFilter, producer, controller); } else { this.attachGraphWriter(producer, producer, controller); } } } private boolean writeTraceEquivalenceReport(final String outputFnPrefixL, final TraceEquivalenceClassFilter traceEquivFilter) throws IOException { boolean retVal = true; final String outputFn = new File(outputFnPrefixL).getCanonicalPath(); PrintStream ps = null; try { ps = new PrintStream(new FileOutputStream(outputFn), false, ENCODING); int numClasses = 0; final Map<ExecutionTrace, Integer> classMap = traceEquivFilter.getEquivalenceClassMap(); // NOPMD (UseConcurrentHashMap) for (final Entry<ExecutionTrace, Integer> e : classMap.entrySet()) { final ExecutionTrace t = e.getKey(); ps.println("Class " + numClasses++ + " ; cardinality: " + e.getValue() + "; # executions: " + t.getLength() + "; representative: " + t.getTraceId() + "; max. stack depth: " + t.getMaxEss()); } if (LOG.isDebugEnabled()) { LOG.debug(""); LOG.debug("#"); LOG.debug("# Plugin: " + "Trace equivalence report"); LOG.debug("Wrote " + numClasses + " equivalence class" + (numClasses > 1 ? "es" : "") + " to file '" + outputFn + "'"); // NOCS } } catch (final FileNotFoundException e) { LOG.error("File not found", e); retVal = false; } finally { if (ps != null) { ps.close(); } } return retVal; } private static class OptionComparator implements Comparator<Object>, Serializable { private static final long serialVersionUID = 1L; public OptionComparator() { // No code necessary } @Override public int compare(final Object o1, final Object o2) { if (o1 == o2) { // NOPMD return 0; } final int posO1 = Constants.SORTED_OPTION_LIST.indexOf(o1); final int posO2 = Constants.SORTED_OPTION_LIST.indexOf(o2); if (posO1 < posO2) { return -1; } if (posO1 > posO2) { return 1; } return 0; } } }