diff --git a/analysis/src/main/java/org/oceandsl/analysis/RewriteBeforeAndAfterEventsStage.java b/analysis/src/main/java/org/oceandsl/analysis/RewriteBeforeAndAfterEventsStage.java index e1875973552581aaabcde747d30f8192cbb36118..4fbc5b0165a74e7892282cb1f3339f4bb3a2b3ba 100644 --- a/analysis/src/main/java/org/oceandsl/analysis/RewriteBeforeAndAfterEventsStage.java +++ b/analysis/src/main/java/org/oceandsl/analysis/RewriteBeforeAndAfterEventsStage.java @@ -1,5 +1,5 @@ /** - * + * */ package org.oceandsl.analysis; @@ -24,94 +24,97 @@ import teetime.framework.OutputPort; * */ public class RewriteBeforeAndAfterEventsStage extends AbstractConsumerStage<IMonitoringRecord> { - - private OutputPort<IMonitoringRecord> outputPort = this.createOutputPort(IMonitoringRecord.class); - - private File addrlineExecutable; - private Map<String,AddrOutput> addressMap = new HashMap<String,AddrOutput>(); - private File modelExecutable; - - public RewriteBeforeAndAfterEventsStage(File addrLineExecutable, File modelExecutable) { - this.addrlineExecutable = addrLineExecutable; - this.modelExecutable = modelExecutable; - } - - @Override - protected void execute(IMonitoringRecord element) throws Exception { - if (element instanceof BeforeOperationEvent) { - BeforeOperationEvent before = (BeforeOperationEvent)element; - AddrOutput rewriteInfo = findRewriteInfo(before.getOperationSignature()); - this.outputPort.send(new BeforeOperationEvent(before.getTimestamp(), before.getTraceId(), before.getOrderIndex(), - rewriteInfo.name,"COMPONENT")); - } else if (element instanceof AfterOperationEvent) { - AfterOperationEvent before = (AfterOperationEvent)element; - AddrOutput rewriteInfo = findRewriteInfo(before.getOperationSignature()); - this.outputPort.send(new AfterOperationEvent(before.getTimestamp(), before.getTraceId(), before.getOrderIndex(), - rewriteInfo.name,"COMPONENT")); - } else - this.outputPort.send(element); - } - - private AddrOutput findRewriteInfo(String address) throws IOException, InterruptedException { - AddrOutput addrOutput = addressMap.get(address); - if (addrOutput == null) { - Process process = Runtime.getRuntime().exec(String.format("%s -e %s -p -C -f %s", - this.addrlineExecutable.getCanonicalPath(), - this.modelExecutable, address)); - process.waitFor(); - new BufferedReader(new InputStreamReader(process.getErrorStream())).lines().forEach(new Consumer<String>() { - @Override - public void accept(String arg0) { - System.err.println(arg0); - } - }); - - new BufferedReader(new InputStreamReader(process.getInputStream())).lines().forEach(new Consumer<String>() { - - final Pattern pattern = Pattern.compile("^(\\w+) at ([\\w\\?/\\.\\-]+):([\\d\\?]*)$"); - - @Override - public void accept(String string) { - Matcher matcher = this.pattern.matcher(string); - if (matcher.find()) { - Integer linenumber = matcher.group(3).equals("?")?null:Integer.parseInt(matcher.group(3)); - addressMap.put(address,new AddrOutput(matcher.group(1), matcher.group(2), linenumber)); - } - } - - }); - return addressMap.get(address); - } else - return addrOutput; - } - - class AddrOutput { - - private final String name; - private final String filename; - private final Integer linenumber; - - public AddrOutput(String name, String filename, Integer linenumber) { - this.name = name; - this.filename = filename; - this.linenumber = linenumber; - } - - public String getName() { - return name; - } - - public String getFilename() { - return filename; - } - - public Integer getLinenumber() { - return linenumber; - } - } - - public OutputPort<IMonitoringRecord> getOutputPort() { - return this.outputPort; - } + + private final OutputPort<IMonitoringRecord> outputPort = this.createOutputPort(IMonitoringRecord.class); + + private final File addrlineExecutable; + private final Map<String, AddrOutput> addressMap = new HashMap<>(); + private final File modelExecutable; + + public RewriteBeforeAndAfterEventsStage(final File addrLineExecutable, final File modelExecutable) { + this.addrlineExecutable = addrLineExecutable; + this.modelExecutable = modelExecutable; + } + + @Override + protected void execute(final IMonitoringRecord element) throws Exception { + if (element instanceof BeforeOperationEvent) { + final BeforeOperationEvent before = (BeforeOperationEvent) element; + final AddrOutput rewriteInfo = this.findRewriteInfo(before.getOperationSignature()); + this.outputPort.send(new BeforeOperationEvent(before.getTimestamp(), before.getTraceId(), + before.getOrderIndex(), rewriteInfo.name, rewriteInfo.getFilename())); + } else if (element instanceof AfterOperationEvent) { + final AfterOperationEvent before = (AfterOperationEvent) element; + final AddrOutput rewriteInfo = this.findRewriteInfo(before.getOperationSignature()); + this.outputPort.send(new AfterOperationEvent(before.getTimestamp(), before.getTraceId(), + before.getOrderIndex(), rewriteInfo.name, rewriteInfo.getFilename())); + } else { + this.outputPort.send(element); + } + } + + private AddrOutput findRewriteInfo(final String address) throws IOException, InterruptedException { + final AddrOutput addrOutput = this.addressMap.get(address); + if (addrOutput == null) { + final Process process = Runtime.getRuntime().exec(String.format("%s -e %s -p -C -f %s", + this.addrlineExecutable.getCanonicalPath(), this.modelExecutable, address)); + process.waitFor(); + new BufferedReader(new InputStreamReader(process.getErrorStream())).lines().forEach(new Consumer<String>() { + @Override + public void accept(final String arg0) { + System.err.println(arg0); + } + }); + + new BufferedReader(new InputStreamReader(process.getInputStream())).lines().forEach(new Consumer<String>() { + + final Pattern pattern = Pattern.compile("^(\\w+) at ([\\w\\?/\\.\\-]+):([\\d\\?]*)$"); + + @Override + public void accept(final String string) { + final Matcher matcher = this.pattern.matcher(string); + if (matcher.find()) { + final Integer linenumber = matcher.group(3).equals("?") ? null + : Integer.parseInt(matcher.group(3)); + RewriteBeforeAndAfterEventsStage.this.addressMap.put(address, + new AddrOutput(matcher.group(1), matcher.group(2), linenumber)); + } + } + + }); + return this.addressMap.get(address); + } else { + return addrOutput; + } + } + + class AddrOutput { + + private final String name; + private final String filename; + private final Integer linenumber; + + public AddrOutput(final String name, final String filename, final Integer linenumber) { + this.name = name; + this.filename = filename; + this.linenumber = linenumber; + } + + public String getName() { + return this.name; + } + + public String getFilename() { + return this.filename; + } + + public Integer getLinenumber() { + return this.linenumber; + } + } + + public OutputPort<IMonitoringRecord> getOutputPort() { + return this.outputPort; + } } diff --git a/common/build.gradle b/common/build.gradle new file mode 100644 index 0000000000000000000000000000000000000000..e6f782cc4285e6dbfaeb8d89fa71fff883a0a708 --- /dev/null +++ b/common/build.gradle @@ -0,0 +1,25 @@ +/* + * This file was generated by the Gradle 'init' task. + * + * This generated file contains a sample Java project to get you started. + * For more details take a look at the Java Quickstart chapter in the Gradle + * User Manual available at https://docs.gradle.org/6.5/userguide/tutorial_java_projects.html + */ + +plugins { + id 'java' + id 'java-library' +} + +dependencies { + implementation 'net.kieker-monitoring:kieker:1.15-SNAPSHOT:jar' +} + +sourceSets { + main { + java { } + } + gen { + java { } + } +} \ No newline at end of file diff --git a/common/src/gen/java/org/oceandsl/common/OperationCallEvent.java b/common/src/gen/java/org/oceandsl/common/OperationCallEvent.java new file mode 100644 index 0000000000000000000000000000000000000000..96113650a5e1130a5dc67186fce8dc414bdbea94 --- /dev/null +++ b/common/src/gen/java/org/oceandsl/common/OperationCallEvent.java @@ -0,0 +1,209 @@ +package org.oceandsl.common; + +import java.nio.BufferOverflowException; + +import kieker.common.exception.RecordInstantiationException; +import kieker.common.record.AbstractMonitoringRecord; +import kieker.common.record.io.IValueDeserializer; +import kieker.common.record.io.IValueSerializer; + + +/** + * @author Reiner Jung + * API compatibility: Kieker 1.15.0 + * + * @since 1.0 + */ +public class OperationCallEvent extends AbstractMonitoringRecord { + /** Descriptive definition of the serialization size of the record. */ + public static final int SIZE = TYPE_SIZE_STRING // OperationCallEvent.sourceComponent + + TYPE_SIZE_STRING // OperationCallEvent.sourceOperation + + TYPE_SIZE_STRING // OperationCallEvent.targetComponent + + TYPE_SIZE_STRING; // OperationCallEvent.targetOperation + + public static final Class<?>[] TYPES = { + String.class, // OperationCallEvent.sourceComponent + String.class, // OperationCallEvent.sourceOperation + String.class, // OperationCallEvent.targetComponent + String.class, // OperationCallEvent.targetOperation + }; + + /** property name array. */ + public static final String[] VALUE_NAMES = { + "sourceComponent", + "sourceOperation", + "targetComponent", + "targetOperation", + }; + + /** default constants. */ + public static final String SOURCE_COMPONENT = ""; + public static final String SOURCE_OPERATION = ""; + public static final String TARGET_COMPONENT = ""; + public static final String TARGET_OPERATION = ""; + private static final long serialVersionUID = -5250152168812250464L; + + /** property declarations. */ + private final String sourceComponent; + private final String sourceOperation; + private final String targetComponent; + private final String targetOperation; + + /** + * Creates a new instance of this class using the given parameters. + * + * @param sourceComponent + * sourceComponent + * @param sourceOperation + * sourceOperation + * @param targetComponent + * targetComponent + * @param targetOperation + * targetOperation + */ + public OperationCallEvent(final String sourceComponent, final String sourceOperation, final String targetComponent, final String targetOperation) { + this.sourceComponent = sourceComponent == null?"":sourceComponent; + this.sourceOperation = sourceOperation == null?"":sourceOperation; + this.targetComponent = targetComponent == null?"":targetComponent; + this.targetOperation = targetOperation == null?"":targetOperation; + } + + + /** + * @param deserializer + * The deserializer to use + * @throws RecordInstantiationException + * when the record could not be deserialized + */ + public OperationCallEvent(final IValueDeserializer deserializer) throws RecordInstantiationException { + this.sourceComponent = deserializer.getString(); + this.sourceOperation = deserializer.getString(); + this.targetComponent = deserializer.getString(); + this.targetOperation = deserializer.getString(); + } + + /** + * {@inheritDoc} + */ + @Override + public void serialize(final IValueSerializer serializer) throws BufferOverflowException { + serializer.putString(this.getSourceComponent()); + serializer.putString(this.getSourceOperation()); + serializer.putString(this.getTargetComponent()); + serializer.putString(this.getTargetOperation()); + } + + /** + * {@inheritDoc} + */ + @Override + public Class<?>[] getValueTypes() { + return TYPES; // NOPMD + } + + /** + * {@inheritDoc} + */ + @Override + public String[] getValueNames() { + return VALUE_NAMES; // NOPMD + } + + /** + * {@inheritDoc} + */ + @Override + public int getSize() { + return SIZE; + } + + + /** + * {@inheritDoc} + */ + @Override + public boolean equals(final Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (obj.getClass() != this.getClass()) { + return false; + } + + final OperationCallEvent castedRecord = (OperationCallEvent) obj; + if (this.getLoggingTimestamp() != castedRecord.getLoggingTimestamp()) { + return false; + } + if (!this.getSourceComponent().equals(castedRecord.getSourceComponent())) { + return false; + } + if (!this.getSourceOperation().equals(castedRecord.getSourceOperation())) { + return false; + } + if (!this.getTargetComponent().equals(castedRecord.getTargetComponent())) { + return false; + } + if (!this.getTargetOperation().equals(castedRecord.getTargetOperation())) { + return false; + } + + return true; + } + /** + * {@inheritDoc} + */ + @Override + public int hashCode() { + int code = 0; + code += this.getSourceComponent().hashCode(); + code += this.getSourceOperation().hashCode(); + code += this.getTargetComponent().hashCode(); + code += this.getTargetOperation().hashCode(); + + return code; + } + + public final String getSourceComponent() { + return this.sourceComponent; + } + + + public final String getSourceOperation() { + return this.sourceOperation; + } + + + public final String getTargetComponent() { + return this.targetComponent; + } + + + public final String getTargetOperation() { + return this.targetOperation; + } + + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + String result = "OperationCallEvent: "; + result += "sourceComponent = "; + result += this.getSourceComponent() + ", "; + + result += "sourceOperation = "; + result += this.getSourceOperation() + ", "; + + result += "targetComponent = "; + result += this.getTargetComponent() + ", "; + + result += "targetOperation = "; + result += this.getTargetOperation() + ", "; + + return result; + } +} diff --git a/common/src/gen/java/org/oceandsl/common/OperationCallEventFactory.java b/common/src/gen/java/org/oceandsl/common/OperationCallEventFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..276ad80e301cb66922ed6fcee657de602a674acb --- /dev/null +++ b/common/src/gen/java/org/oceandsl/common/OperationCallEventFactory.java @@ -0,0 +1,35 @@ +package org.oceandsl.common; + + +import kieker.common.exception.RecordInstantiationException; +import kieker.common.record.factory.IRecordFactory; +import kieker.common.record.io.IValueDeserializer; + +/** + * @author Reiner Jung + * + * @since 1.0 + */ +public final class OperationCallEventFactory implements IRecordFactory<OperationCallEvent> { + + + @Override + public OperationCallEvent create(final IValueDeserializer deserializer) throws RecordInstantiationException { + return new OperationCallEvent(deserializer); + } + + + @Override + public String[] getValueNames() { + return OperationCallEvent.VALUE_NAMES; // NOPMD + } + + @Override + public Class<?>[] getValueTypes() { + return OperationCallEvent.TYPES; // NOPMD + } + + public int getRecordSizeInBytes() { + return OperationCallEvent.SIZE; + } +} diff --git a/common/src/main/java/org/oceandsl/common/common.irl b/common/src/main/java/org/oceandsl/common/common.irl new file mode 100644 index 0000000000000000000000000000000000000000..d7d0f72f55d262db22d4e751df468be8d8a41e9e --- /dev/null +++ b/common/src/main/java/org/oceandsl/common/common.irl @@ -0,0 +1,9 @@ +package org.oceandsl.common + +@author "Reiner Jung" @since "1.0" +entity OperationCallEvent { + string sourceComponent + string sourceOperation + string targetComponent + string targetOperation +} \ No newline at end of file diff --git a/common/src/main/java/org/oceandsl/common/package-info.java b/common/src/main/java/org/oceandsl/common/package-info.java new file mode 100644 index 0000000000000000000000000000000000000000..9f28e9374665cbef02a9ae6805141c5661806be4 --- /dev/null +++ b/common/src/main/java/org/oceandsl/common/package-info.java @@ -0,0 +1,20 @@ +/*************************************************************************** + * Copyright (C) 2021 OceanDSL (https://oceandsl.uni-kiel.de) + * + * 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. + ***************************************************************************/ +/** + * @author reiner + * + */ +package org.oceandsl.common; \ No newline at end of file diff --git a/logback.groovy b/logback.groovy new file mode 100644 index 0000000000000000000000000000000000000000..5bf5a90c7ce6c214f71efea03fb7851130220865 --- /dev/null +++ b/logback.groovy @@ -0,0 +1,32 @@ +import ch.qos.logback.classic.filter.ThresholdFilter +import ch.qos.logback.core.filter.EvaluatorFilter +import ch.qos.logback.classic.boolex.GEventEvaluator + +import static ch.qos.logback.core.spi.FilterReply.DENY +import static ch.qos.logback.core.spi.FilterReply.NEUTRAL + +appender("FILE", FileAppender) { + filter(ThresholdFilter) { + level=INFO + } + file = "kieker.log" + append = true + encoder(PatternLayoutEncoder) { + pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + } +} + +appender("STDOUT", ConsoleAppender) { + filter(ThresholdFilter) { + level=INFO + } + encoder(PatternLayoutEncoder) { + pattern = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" + } + target="System.out" +} + +root(DEBUG, ["STDOUT", "FILE"]) + + + diff --git a/logback.xml b/logback.xml new file mode 100644 index 0000000000000000000000000000000000000000..e3ed73c7b561a51ae1da7deccdd8018879e138b4 --- /dev/null +++ b/logback.xml @@ -0,0 +1,14 @@ +<configuration> + + <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> + <!-- encoders are assigned the type + ch.qos.logback.classic.encoder.PatternLayoutEncoder by default --> + <encoder> + <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern> + </encoder> + </appender> + + <root level="debug"> + <appender-ref ref="STDOUT" /> + </root> +</configuration> \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 39b81b8115d9365caaf28bb2e637760a793eb03b..70990e7ea9c2e14d1dfed330211fe07bf8213b9f 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,6 +9,7 @@ rootProject.name = 'oceandsl-java-tools' +include 'common' include 'analysis' include 'tools:rewrite-log-entries' include 'tools:create-architecture-model' diff --git a/tools/create-architecture-model/build.gradle b/tools/create-architecture-model/build.gradle index 52ff287e06886e796e730ab941f74ef958ff28ba..f39b956b120ed072af54f4f8aa105a767a8fc8c8 100644 --- a/tools/create-architecture-model/build.gradle +++ b/tools/create-architecture-model/build.gradle @@ -12,9 +12,14 @@ plugins { } dependencies { + implementation project(':common') implementation project(':analysis') // https://mvnrepository.com/artifact/org.eclipse.emf/org.eclipse.emf.ecore implementation 'org.eclipse.emf:org.eclipse.emf.ecore:2.23.0' + runtime 'org.eclipse.emf:org.eclipse.emf.ecore:2.23.0' + + runtime 'jakarta.xml.bind:jakarta.xml.bind-api:2.3.3' + runtime 'com.sun.xml.bind:jaxb-impl:2.3.3' } application { diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelMain.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelMain.java index 55d5ec728b65aba2e1c76570bc33a98d07dee503..b356bb76b0f1816daf5f9119273dd5a76d208154 100644 --- a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelMain.java +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelMain.java @@ -8,61 +8,80 @@ import java.io.IOException; import com.beust.jcommander.JCommander; +import kieker.analysis.statistics.StatisticsModel; +import kieker.analysisteetime.model.analysismodel.assembly.AssemblyFactory; +import kieker.analysisteetime.model.analysismodel.assembly.AssemblyModel; +import kieker.analysisteetime.model.analysismodel.deployment.DeploymentFactory; +import kieker.analysisteetime.model.analysismodel.deployment.DeploymentModel; +import kieker.analysisteetime.model.analysismodel.execution.ExecutionFactory; +import kieker.analysisteetime.model.analysismodel.execution.ExecutionModel; +import kieker.analysisteetime.model.analysismodel.type.TypeFactory; +import kieker.analysisteetime.model.analysismodel.type.TypeModel; import kieker.common.configuration.Configuration; import kieker.common.exception.ConfigurationException; import kieker.tools.common.AbstractService; -public class ArchitectureModelMain extends AbstractService<TeetimeConfiguration,ArchitectureModelSettings>{ - - public static void main(String[] args) { - java.lang.System.exit(new ArchitectureModelMain().run("Kieker Log ELF Rewriter", - "log-rewriter", args, new ArchitectureModelSettings())); +public class ArchitectureModelMain extends AbstractService<TeetimeConfiguration, ArchitectureModelSettings> { + + private final TypeModel typeModel = TypeFactory.eINSTANCE.createTypeModel(); + private final AssemblyModel assemblyModel = AssemblyFactory.eINSTANCE.createAssemblyModel(); + private final DeploymentModel deploymentModel = DeploymentFactory.eINSTANCE.createDeploymentModel(); + private final ExecutionModel executionModel = ExecutionFactory.eINSTANCE.createExecutionModel(); + private final StatisticsModel statisticsModel = new StatisticsModel(); + + public static void main(final String[] args) { + final ArchitectureModelMain main = new ArchitectureModelMain(); + final int exitCode = main.run("Kieker Log ELF Rewriter", "log-rewriter", args, new ArchitectureModelSettings()); + + java.lang.System.exit(exitCode); } - @Override - protected TeetimeConfiguration createTeetimeConfiguration() throws ConfigurationException { - try { - return new TeetimeConfiguration(this.parameterConfiguration); - } catch (IOException e) { - throw new ConfigurationException(e); - } - } + @Override + protected TeetimeConfiguration createTeetimeConfiguration() throws ConfigurationException { + try { + return new TeetimeConfiguration(this.parameterConfiguration, this.typeModel, this.assemblyModel, + this.deploymentModel, this.executionModel, this.statisticsModel); + } catch (final IOException e) { + throw new ConfigurationException(e); + } + } - @Override - protected File getConfigurationFile() { - // we do not use a configuration file - return null; - } + @Override + protected File getConfigurationFile() { + // we do not use a configuration file + return null; + } - @Override - protected boolean checkConfiguration(Configuration configuration, JCommander commander) { - return true; - } + @Override + protected boolean checkConfiguration(final Configuration configuration, final JCommander commander) { + return true; + } + + @Override + protected boolean checkParameters(final JCommander commander) throws ConfigurationException { + if (!this.parameterConfiguration.getAddrlineExecutable().canExecute()) { + this.logger.error("Addr2line file {} is not executable", + this.parameterConfiguration.getAddrlineExecutable()); + return false; + } + if (!this.parameterConfiguration.getModelExecutable().canExecute()) { + this.logger.error("Model file {} is not executable", this.parameterConfiguration.getModelExecutable()); + return false; + } + if (!this.parameterConfiguration.getInputFile().isDirectory()) { + this.logger.error("Input directory {} is not directory", this.parameterConfiguration.getInputFile()); + return false; + } + if (!this.parameterConfiguration.getOutputFile().isDirectory()) { + this.logger.error("Output directory {} is not directory", this.parameterConfiguration.getOutputFile()); + return false; + } + return true; + } - @Override - protected boolean checkParameters(JCommander commander) throws ConfigurationException { - if (!this.parameterConfiguration.getAddrlineExecutable().canExecute()) { - this.logger.error("Addr2line file {} is not executable", this.parameterConfiguration.getAddrlineExecutable()); - return false; - } - if (!this.parameterConfiguration.getModelExecutable().canExecute()) { - this.logger.error("Model file {} is not executable", this.parameterConfiguration.getModelExecutable()); - return false; - } - if (!this.parameterConfiguration.getInputFile().isDirectory()) { - this.logger.error("Input directory {} is not directory", this.parameterConfiguration.getInputFile()); - return false; - } - if (!this.parameterConfiguration.getOutputFile().isDirectory()) { - this.logger.error("Output directory {} is not directory", this.parameterConfiguration.getOutputFile()); - return false; - } - return true; - } + @Override + protected void shutdownService() { + // TODO Auto-generated method stub - @Override - protected void shutdownService() { - // TODO Auto-generated method stub - - } + } } diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelSettings.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelSettings.java index c353bc0a88d55f7a95120b1782c52bc1fedc88af..60320e058a9bdf2bce0e718e9477523c0532d30c 100644 --- a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelSettings.java +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ArchitectureModelSettings.java @@ -1,5 +1,5 @@ /** - * + * */ package org.oceandsl.architecture.model; @@ -14,31 +14,43 @@ import com.beust.jcommander.converters.FileConverter; */ public class ArchitectureModelSettings { - @Parameter(names = { "-i", "--input" }, required = true, converter = FileConverter.class, description = "Input Kieker log directory") - private File inputFile; - - @Parameter(names = { "-o", "--output" }, required = true, converter = FileConverter.class, description = "Output directory where to put the graphics") - private File outputFile; - - @Parameter(names = { "-a", "--addrline" }, required = true, converter = FileConverter.class, description = "Location of the addrline tool") - private File addrlineExecutable; - - @Parameter(names = { "-e", "--executable" }, required = true, converter = FileConverter.class, description = "Location of the executable") - private File modelExecutable; - - public File getInputFile() { - return this.inputFile; - } - - public File getOutputFile() { - return this.outputFile; - } - - public File getAddrlineExecutable() { - return this.addrlineExecutable; - } - - public File getModelExecutable() { - return this.modelExecutable; - } + @Parameter(names = { "-i", + "--input" }, required = true, converter = FileConverter.class, description = "Input Kieker log directory") + private File inputFile; + + @Parameter(names = { "-o", + "--output" }, required = true, converter = FileConverter.class, description = "Output directory where to put the graphics") + private File outputFile; + + @Parameter(names = { "-a", + "--addrline" }, required = true, converter = FileConverter.class, description = "Location of the addrline tool") + private File addrlineExecutable; + + @Parameter(names = { "-e", + "--executable" }, required = true, converter = FileConverter.class, description = "Location of the executable") + private File modelExecutable; + + @Parameter(names = { "-p", + "--prefix" }, required = false, description = "Path prefix to be removed from filenames in the visualization") + private String prefix; + + public File getInputFile() { + return this.inputFile; + } + + public File getOutputFile() { + return this.outputFile; + } + + public File getAddrlineExecutable() { + return this.addrlineExecutable; + } + + public File getModelExecutable() { + return this.modelExecutable; + } + + public String getPrefix() { + return this.prefix; + } } diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/Call.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/Call.java deleted file mode 100644 index 6ec8ead93fb02355e45d4f82da19e313f5c770fb..0000000000000000000000000000000000000000 --- a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/Call.java +++ /dev/null @@ -1,59 +0,0 @@ -/** - * - */ -package org.oceandsl.architecture.model; - -/** - * @author reiner - * - */ -public class Call { - - private final String fromClassSignature; - private final String fromOperationSignature; - private final String toClassSignature; - private final String toOperationSignature; - - public Call(ECallType before, String fromClassSignature, String fromOperationSignature, String toClassSignature, - String toOperationSignature) { - this.fromClassSignature = fromClassSignature; - this.fromOperationSignature = fromOperationSignature; - this.toClassSignature = toClassSignature; - this.toOperationSignature = toOperationSignature; - } - - public final String getFromClassSignature() { - return fromClassSignature; - } - - public final String getFromOperationSignature() { - return fromOperationSignature; - } - - public final String getToClassSignature() { - return toClassSignature; - } - - public final String getToOperationSignature() { - return toOperationSignature; - } - - @Override - public boolean equals(Object object) { - if (object instanceof Call) { - Call otherCall = (Call)object; - return compareValues(this.fromClassSignature, otherCall.getFromClassSignature()) && - compareValues(this.fromOperationSignature, otherCall.getFromOperationSignature()) && - compareValues(this.toClassSignature, otherCall.getToClassSignature()) && - compareValues(this.toOperationSignature, otherCall.getToOperationSignature()); - } else - return false; - } - - private boolean compareValues(String from, String to) { - if (from == null && to == null) - return true; - return from != null && to != null && from.equals(to); - } - -} diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CountUniqueCalls.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CountUniqueCalls.java index f819fd675d0841538acb325a82fdb90886027d08..d52c189e2fb7099e0575ae2fabac2c4bc103e382 100644 --- a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CountUniqueCalls.java +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CountUniqueCalls.java @@ -3,72 +3,34 @@ */ package org.oceandsl.architecture.model; -import java.util.ArrayList; -import java.util.List; +import java.util.function.Function; -import teetime.framework.AbstractConsumerStage; -import teetime.framework.OutputPort; +import kieker.analysis.statistics.StatisticsDecoratorStage; +import kieker.analysis.statistics.StatisticsModel; +import kieker.analysis.statistics.Units; +import kieker.analysis.statistics.calculating.CountCalculator; +import kieker.analysis.util.ComposedKey; +import kieker.analysisteetime.model.analysismodel.deployment.DeployedOperation; +import kieker.analysisteetime.model.analysismodel.execution.ExecutionModel; /** * @author reiner * */ -public class CountUniqueCalls extends AbstractConsumerStage<Call> { +public class CountUniqueCalls extends StatisticsDecoratorStage<OperationCall> { - private final List<StoredCall> storedCalls = new ArrayList<>(); - private final OutputPort<List<StoredCall>> outputPort = this.createOutputPort(); - - @Override - protected void execute(final Call element) throws Exception { - for (final StoredCall storedCall : this.storedCalls) { - if (storedCall.getCall().equals(element)) { - storedCall.increment(); - return; - } - } - this.storedCalls.add(new StoredCall(1, element)); - } - - private void printStore() { - for (final StoredCall storedCall : this.storedCalls) { - final Call call = storedCall.getCall(); - this.logger.info( - String.format("%s:%s -> %s:%s (%d)", call.getFromClassSignature(), call.getFromOperationSignature(), - call.getToClassSignature(), call.getToOperationSignature(), storedCall.getCount())); - } - } - - @Override - protected void onTerminating() { - this.printStore(); - this.outputPort.send(this.storedCalls); - super.onTerminating(); - } - - private class StoredCall { - private long count; - private final Call call; - - public StoredCall(final long count, final Call call) { - this.count = count; - this.call = call; - } - - public Call getCall() { - return this.call; - } - - public void increment() { - this.count++; - } - - public long getCount() { - return this.count; - } + public CountUniqueCalls(final StatisticsModel statisticsModel, final ExecutionModel executionModel) { + super(statisticsModel, Units.RESPONSE_TIME, new CountCalculator<>(), + CountUniqueCalls.createForAggregatedInvocation(executionModel)); } - public OutputPort<List<StoredCall>> getOutputPort() { - return this.outputPort; + public static final Function<OperationCall, Object> createForAggregatedInvocation( + final ExecutionModel executionModel) { + return operationCall -> { + final ComposedKey<DeployedOperation, DeployedOperation> key = ComposedKey + .of(operationCall.getSourceOperation(), operationCall.getTargetOperation()); + return executionModel.getAggregatedInvocations().get(key); + }; } } diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CreateCallsStage.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CreateCallsStage.java index e4ee16b07e80ee27b845cc2b6b750bd7108232ef..c642fb713fb8c63b2e0743fb2109a5d44f89d8db 100644 --- a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CreateCallsStage.java +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/CreateCallsStage.java @@ -1,12 +1,21 @@ /** - * + * */ package org.oceandsl.architecture.model; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.Stack; +import kieker.analysisteetime.model.analysismodel.deployment.DeployedComponent; +import kieker.analysisteetime.model.analysismodel.deployment.DeployedOperation; +import kieker.analysisteetime.model.analysismodel.deployment.DeploymentContext; +import kieker.analysisteetime.model.analysismodel.deployment.DeploymentModel; import kieker.common.record.flow.IFlowRecord; +import kieker.common.record.flow.trace.TraceMetadata; +import kieker.common.record.flow.trace.operation.AfterOperationEvent; import kieker.common.record.flow.trace.operation.BeforeOperationEvent; import teetime.framework.AbstractConsumerStage; import teetime.framework.OutputPort; @@ -16,30 +25,66 @@ import teetime.framework.OutputPort; * */ public class CreateCallsStage extends AbstractConsumerStage<IFlowRecord> { - - private final Map<Long,BeforeOperationEvent> cachedTraces = new HashMap<>(); - private OutputPort<Call> outputPort = this.createOutputPort(Call.class); - - @Override - protected void execute(IFlowRecord element) throws Exception { - if (element instanceof BeforeOperationEvent) { - BeforeOperationEvent currentEvent = (BeforeOperationEvent)element; - BeforeOperationEvent previousEvent = cachedTraces.get(currentEvent.getTraceId()); - if (previousEvent != null) { - this.outputPort.send(new Call(ECallType.BEFORE, - previousEvent.getClassSignature(), - previousEvent.getOperationSignature(), - currentEvent.getClassSignature(), - currentEvent.getOperationSignature())); - - } - cachedTraces.put(currentEvent.getTraceId(), currentEvent); - } - } - - - public OutputPort<Call> getOutputPort() { - return outputPort; - } + + private final Map<Long, Stack<BeforeOperationEvent>> cachedTraces = new HashMap<>(); + private final List<TraceMetadata> traces = new ArrayList<>(); + private final OutputPort<OperationCall> outputPort = this.createOutputPort(OperationCall.class); + private final DeploymentModel deploymentModel; + + public CreateCallsStage(final DeploymentModel deploymentModel) { + this.deploymentModel = deploymentModel; + } + + @Override + protected void execute(final IFlowRecord element) throws Exception { + if (element instanceof TraceMetadata) { + final TraceMetadata metadata = (TraceMetadata) element; + this.traces.add(metadata); + this.cachedTraces.put(metadata.getTraceId(), new Stack<BeforeOperationEvent>()); + } else if (element instanceof BeforeOperationEvent) { + final BeforeOperationEvent currentEvent = (BeforeOperationEvent) element; + final Stack<BeforeOperationEvent> eventStack = this.cachedTraces.get(currentEvent.getTraceId()); + if (eventStack.size() > 0) { + final BeforeOperationEvent previousEvent = eventStack.peek(); + final DeploymentContext deploymentContext = this.deploymentModel.getDeploymentContexts() + .get(this.findDeploymentContext(currentEvent)); + final DeployedComponent sourceComponent = deploymentContext.getComponents() + .get(previousEvent.getClassSignature()); + final DeployedComponent targetComponent = deploymentContext.getComponents() + .get(currentEvent.getClassSignature()); + + final DeployedOperation sourceOperation = sourceComponent.getContainedOperations() + .get(previousEvent.getOperationSignature()); + final DeployedOperation targetOperation = targetComponent.getContainedOperations() + .get(currentEvent.getOperationSignature()); + + this.outputPort.send(new OperationCall(sourceOperation, targetOperation)); + } + eventStack.push(currentEvent); + } else if (element instanceof AfterOperationEvent) { + final AfterOperationEvent currentEvent = (AfterOperationEvent) element; + final Stack<BeforeOperationEvent> eventStack = this.cachedTraces.get(currentEvent.getTraceId()); + final BeforeOperationEvent beforeOperationEvent = eventStack.pop(); + if (!beforeOperationEvent.getOperationSignature().equals(currentEvent.getOperationSignature())) { + // broken trace + this.logger.error("Trace is broken. Found {}, expected {}", currentEvent.getOperationSignature(), + beforeOperationEvent.getOperationSignature()); + eventStack.push(beforeOperationEvent); + } + } + } + + private String findDeploymentContext(final BeforeOperationEvent currentEvent) { + for (final TraceMetadata trace : this.traces) { + if (trace.getTraceId() == currentEvent.getTraceId()) { + return trace.getHostname(); + } + } + return ""; + } + + public OutputPort<OperationCall> getOutputPort() { + return this.outputPort; + } } diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/DedicatedFileNameMapper.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/DedicatedFileNameMapper.java new file mode 100644 index 0000000000000000000000000000000000000000..3e925bda1aefdf1ac0f52816057be3891e42b8d7 --- /dev/null +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/DedicatedFileNameMapper.java @@ -0,0 +1,35 @@ +package org.oceandsl.architecture.model; + +import java.util.function.Function; + +import kieker.analysis.graph.IGraph; +import kieker.analysis.graph.util.FileExtension; + +public class DedicatedFileNameMapper implements Function<IGraph, String> { + + private final String outputDirectory; + private final FileExtension fileExtension; + private final String outputFilename; + + /** + * Create a simple file mapper. + * + * @param outputDirectory + * output directory path + * @param outputFilename + * filename + * @param fileExtension + * file extension for the graph + */ + public DedicatedFileNameMapper(final String outputDirectory, final String outputFilename, + final FileExtension fileExtension) { + this.outputDirectory = outputDirectory; + this.outputFilename = outputFilename; + this.fileExtension = fileExtension; + } + + @Override + public String apply(final IGraph graph) { + return this.outputDirectory + '/' + this.outputFilename + "-" + graph.getName() + '.' + this.fileExtension; + } +} diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/DotExportConfigurationFactory.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/DotExportConfigurationFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..4d0803a9bc115f34f36dbece24f618aa077b547f --- /dev/null +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/DotExportConfigurationFactory.java @@ -0,0 +1,296 @@ +/*************************************************************************** + * Copyright 2020 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 org.oceandsl.architecture.model; + +import java.util.Collection; + +import kieker.analysis.graph.IElement; +import kieker.analysis.graph.IVertex; +import kieker.analysis.graph.dependency.PropertyConstants; +import kieker.analysis.graph.dependency.vertextypes.IVertexTypeMapper; +import kieker.analysis.graph.dependency.vertextypes.VertexType; +import kieker.analysis.graph.export.dot.DotExportConfiguration; +import kieker.analysis.graph.util.dot.attributes.DotClusterAttribute; +import kieker.analysis.graph.util.dot.attributes.DotEdgeAttribute; +import kieker.analysis.graph.util.dot.attributes.DotGraphAttribute; +import kieker.analysis.graph.util.dot.attributes.DotNodeAttribute; +import kieker.analysis.signature.NameBuilder; + +/** + * @author Sören Henning + * + * @since 1.14 + */ +public class DotExportConfigurationFactory { + + private static final String ENTRY_LABEL = "'Entry'"; + + private final NameBuilder nameBuilder; + private final IVertexTypeMapper vertexTypeMapper; + + /** + * Uses TO_STRING of {@link kieker.analysis.graph.dependency.vertextypes.IVertexTypeMapper} as + * second default argument. + * + * @param nameBuilder + * label for the builder + */ + public DotExportConfigurationFactory(final NameBuilder nameBuilder) { + this(nameBuilder, IVertexTypeMapper.TO_STRING); + } + + public DotExportConfigurationFactory(final NameBuilder nameBuilder, final IVertexTypeMapper vertexTypeMapper) { + this.nameBuilder = nameBuilder; + this.vertexTypeMapper = vertexTypeMapper; + } + + private DotExportConfiguration.Builder createBaseBuilder() { + final DotExportConfiguration.Builder builder = new DotExportConfiguration.Builder(); + + builder.addGraphAttribute(DotGraphAttribute.RANKDIR, g -> "LR"); + builder.addDefaultEdgeAttribute(DotEdgeAttribute.STYLE, g -> "solid"); + builder.addDefaultEdgeAttribute(DotEdgeAttribute.ARROWHEAD, g -> "open"); + builder.addDefaultEdgeAttribute(DotEdgeAttribute.COLOR, g -> "#000000"); + + builder.addDefaultNodeAttribute(DotNodeAttribute.STYLE, g -> "filled"); + builder.addDefaultNodeAttribute(DotNodeAttribute.COLOR, g -> "#000000"); + builder.addDefaultNodeAttribute(DotNodeAttribute.FILLCOLOR, g -> "white"); + + builder.addEdgeAttribute(DotEdgeAttribute.LABEL, e -> this.getProperty(e, PropertyConstants.CALLS).toString()); + + builder.addClusterAttribute(DotClusterAttribute.STYLE, v -> "filled"); + builder.addClusterAttribute(DotClusterAttribute.FILLCOLOR, v -> "white"); + + return builder; + } + + public DotExportConfiguration createForTypeLevelOperationDependencyGraph() { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "oval"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return new StringBuilder().append(this.createOperationLabelFromVertex(v)).append("\\n") + .append(this.createStatisticsFromVertex(v)).toString(); + } + }); + + builder.addClusterAttribute(DotClusterAttribute.LABEL, v -> this.createComponentLabelFromVertex(v).toString()); + + return builder.build(); + } + + public DotExportConfiguration createForTypeLevelComponentDependencyGraph() { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "box"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return new StringBuilder().append(this.createComponentLabelFromVertex(v)).append("\\n") + .append(this.createStatisticsFromVertex(v)).toString(); + } + }); + + return builder.build(); + } + + public DotExportConfiguration createForAssemblyLevelOperationDependencyGraph(final boolean vertexStatistics) { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "oval"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return new StringBuilder().append(this.createOperationLabelFromVertex(v)).append("\\n") + .append(vertexStatistics ? this.createStatisticsFromVertex(v) : "").toString(); + } + }); + + builder.addClusterAttribute(DotClusterAttribute.LABEL, v -> this.createComponentLabelFromVertex(v).toString()); + + return builder.build(); + } + + public DotExportConfiguration createForAssemblyLevelComponentDependencyGraph(final boolean vertexStatistics) { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "box"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return new StringBuilder().append(this.createComponentLabelFromVertex(v)).append("\\n") + .append(vertexStatistics ? this.createStatisticsFromVertex(v) : "").toString(); + } + }); + + return builder.build(); + } + + public DotExportConfiguration createForDeploymentLevelOperationDependencyGraph() { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "oval"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return new StringBuilder().append(this.createOperationLabelFromVertex(v)).append("\\n") + .append(this.createStatisticsFromVertex(v)).toString(); + } + }); + + builder.addClusterAttribute(DotClusterAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + switch (type) { + case DEPLOYMENT_CONTEXT: + return this.createContextLabelFromVertex(v).toString(); + case DEPLOYED_COMPONENT: + return this.createComponentLabelFromVertex(v).toString(); + default: + throw new IllegalArgumentException( + "Type '" + type.toString() + "' is not supported for this dependency graph."); + } + }); + + return builder.build(); + } + + public DotExportConfiguration createForDeploymentLevelComponentDependencyGraph() { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "box"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return new StringBuilder().append(this.createComponentLabelFromVertex(v)).append("\\n") + .append(this.createStatisticsFromVertex(v)).toString(); + } + }); + + builder.addClusterAttribute(DotClusterAttribute.LABEL, v -> this.createContextLabelFromVertex(v).toString()); + + return builder.build(); + } + + public DotExportConfiguration createForDeploymentLevelContextDependencyGraph() { + final DotExportConfiguration.Builder builder = this.createBaseBuilder(); + + builder.addDefaultNodeAttribute(DotNodeAttribute.SHAPE, v -> "box3d"); + + builder.addNodeAttribute(DotNodeAttribute.LABEL, v -> { + final VertexType type = this.getProperty(v, PropertyConstants.TYPE, VertexType.class); + if (type == VertexType.ENTRY) { + return DotExportConfigurationFactory.ENTRY_LABEL; + } else { + return this.createContextLabelFromVertex(v).toString(); + } + }); + + return builder.build(); + } + + private StringBuilder createType(final VertexType type) { + return new StringBuilder().append("<<").append(this.vertexTypeMapper.apply(type)).append(">>"); + } + + private StringBuilder createOperationLabelFromVertex(final IVertex vertex) { + @SuppressWarnings("unchecked") + final Collection<String> modifiers = this.getProperty(vertex, PropertyConstants.MODIFIERS, Collection.class); + final String returnType = this.getProperty(vertex, PropertyConstants.RETURN_TYPE, String.class); + final String name = this.getProperty(vertex, PropertyConstants.NAME, String.class); + @SuppressWarnings("unchecked") + final Collection<String> parameterTypes = this.getProperty(vertex, PropertyConstants.PARAMETER_TYPES, + Collection.class); + + return new StringBuilder( + this.nameBuilder.getOperationNameBuilder().build(modifiers, returnType, name, parameterTypes)); + } + + private StringBuilder createComponentLabelFromVertex(final IVertex vertex) { + final VertexType type = this.getProperty(vertex, PropertyConstants.TYPE, VertexType.class); + final String name = this.getProperty(vertex, PropertyConstants.NAME, String.class); + final String packageName = this.getProperty(vertex, PropertyConstants.PACKAGE_NAME, String.class); + + return new StringBuilder().append(this.createType(type)).append("\\n") + .append(this.nameBuilder.getComponentNameBuilder().build(packageName, name)); + } + + private StringBuilder createContextLabelFromVertex(final IVertex vertex) { + final VertexType type = this.getProperty(vertex, PropertyConstants.TYPE, VertexType.class); + final String name = this.getProperty(vertex, PropertyConstants.NAME, String.class); + + return new StringBuilder().append(this.createType(type)).append("\\n").append(name); + } + + private StringBuilder createStatisticsFromVertex(final IVertex vertex) { + final String timeUnit = this.getProperty(vertex, PropertyConstants.TIME_UNIT).toString(); + final String minResponseTime = this.getProperty(vertex, PropertyConstants.MIN_REPSONSE_TIME).toString(); + final String maxResponseTime = this.getProperty(vertex, PropertyConstants.MAX_REPSONSE_TIME).toString(); + final String totalResponseTime = this.getProperty(vertex, PropertyConstants.TOTAL_RESPONSE_TIME).toString(); + final String meanResponseTime = this.getProperty(vertex, PropertyConstants.MEAN_REPSONSE_TIME).toString(); + final String medianResponseTime = this.getProperty(vertex, PropertyConstants.MEDIAN_REPSONSE_TIME).toString(); + return this.createStatistics(timeUnit, minResponseTime, maxResponseTime, totalResponseTime, meanResponseTime, + medianResponseTime); + } + + private StringBuilder createStatistics(final String timeUnit, final String minResponseTime, + final String maxResponseTime, final String totalResponseTime, final String meanResponseTime, + final String medianResponseTime) { + return new StringBuilder().append("min: ").append(minResponseTime).append(' ').append(timeUnit).append(", ") + .append("max: ").append(maxResponseTime).append(' ').append(timeUnit).append(", ").append("total: ") + .append(totalResponseTime).append(' ').append(timeUnit).append(",\\n").append("avg: ") + .append(meanResponseTime).append(' ').append(timeUnit).append(", ").append("median: ") + .append(medianResponseTime).append(' ').append(timeUnit); + } + + // BETTER This could be moved to the graph library + private Object getProperty(final IElement element, final String key) { + final Object object = element.getProperty(key); + if (object == null) { + throw new IllegalArgumentException("There is no key '" + key + "' for element '" + element + "'"); + } + return object; + } + + // BETTER This could be moved to the graph library + private <T> T getProperty(final IElement element, final String key, final Class<T> clazz) { + final Object object = this.getProperty(element, key); + if (!clazz.isInstance(object)) { + throw new IllegalArgumentException("Object with key '" + key + "' is not of type '" + clazz + "'"); + } + return clazz.cast(object); + } + +} diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ExecutionModelGenerationStage.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ExecutionModelGenerationStage.java new file mode 100644 index 0000000000000000000000000000000000000000..cbcdb8aea70edbd7134c1a69086395cf02234f7f --- /dev/null +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ExecutionModelGenerationStage.java @@ -0,0 +1,44 @@ +package org.oceandsl.architecture.model; + +import kieker.analysis.util.ComposedKey; +import kieker.analysisteetime.model.analysismodel.deployment.DeployedOperation; +import kieker.analysisteetime.model.analysismodel.execution.AggregatedInvocation; +import kieker.analysisteetime.model.analysismodel.execution.ExecutionFactory; +import kieker.analysisteetime.model.analysismodel.execution.ExecutionModel; +import teetime.framework.AbstractConsumerStage; +import teetime.framework.OutputPort; + +public class ExecutionModelGenerationStage extends AbstractConsumerStage<OperationCall> { + + private final ExecutionFactory factory = ExecutionFactory.eINSTANCE; + + private final ExecutionModel executionModel; + + private final OutputPort<OperationCall> outputPort = this.createOutputPort(OperationCall.class); + + public ExecutionModelGenerationStage(final ExecutionModel executionModel) { + this.executionModel = executionModel; + } + + @Override + protected void execute(final OperationCall call) throws Exception { + this.addExecution(call.getSourceOperation(), call.getTargetOperation()); + this.outputPort.send(call); + } + + protected void addExecution(final DeployedOperation source, final DeployedOperation target) { + final ComposedKey<DeployedOperation, DeployedOperation> key = ComposedKey.of(source, target); + if (!this.executionModel.getAggregatedInvocations().containsKey(key)) { + final AggregatedInvocation invocation = this.factory.createAggregatedInvocation(); + invocation.setSource(source); + invocation.setTarget(target); + + this.executionModel.getAggregatedInvocations().put(key, invocation); + } + } + + public OutputPort<OperationCall> getOutputPort() { + return this.outputPort; + } + +} diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/OperationCall.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/OperationCall.java new file mode 100644 index 0000000000000000000000000000000000000000..bcac547b6e94c4cfdf95afba6c024242f07365fa --- /dev/null +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/OperationCall.java @@ -0,0 +1,41 @@ +/** + * + */ +package org.oceandsl.architecture.model; + +import kieker.analysisteetime.model.analysismodel.deployment.DeployedOperation; + +/** + * @author reiner + * + */ +public class OperationCall { + + private final DeployedOperation sourceOperation; + private final DeployedOperation targetOperation; + + public OperationCall(final DeployedOperation sourceOperation, final DeployedOperation targetOperation) { + this.sourceOperation = sourceOperation; + this.targetOperation = targetOperation; + } + + public DeployedOperation getSourceOperation() { + return this.sourceOperation; + } + + public DeployedOperation getTargetOperation() { + return this.targetOperation; + } + + @Override + public boolean equals(final Object object) { + if (object instanceof OperationCall) { + final OperationCall otherCall = (OperationCall) object; + return (otherCall.getSourceOperation().equals(this.getSourceOperation()) + && otherCall.getTargetOperation().equals(this.getTargetOperation())); + } else { + return false; + } + } + +} diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ProduceBeforeAndAfterEventsFromOperationCalls.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ProduceBeforeAndAfterEventsFromOperationCalls.java new file mode 100644 index 0000000000000000000000000000000000000000..8e8f55d2567654cfd4d3cb64febe3837607cb2ed --- /dev/null +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/ProduceBeforeAndAfterEventsFromOperationCalls.java @@ -0,0 +1,60 @@ +/*************************************************************************** + * Copyright (C) 2021 OceanDSL (https://oceandsl.uni-kiel.de) + * + * 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 org.oceandsl.architecture.model; + +import org.oceandsl.common.OperationCallEvent; + +import kieker.common.record.IMonitoringRecord; +import kieker.common.record.flow.IFlowRecord; +import kieker.common.record.flow.trace.TraceMetadata; +import kieker.common.record.flow.trace.operation.AfterOperationEvent; +import kieker.common.record.flow.trace.operation.BeforeOperationEvent; +import teetime.stage.basic.AbstractTransformation; + +/** + * @author reiner + * + */ +public class ProduceBeforeAndAfterEventsFromOperationCalls + extends AbstractTransformation<IMonitoringRecord, IFlowRecord> { + + private long traceId = 1; + private long now = 0; + private final String hostname; + + public ProduceBeforeAndAfterEventsFromOperationCalls(final String hostname) { + this.hostname = hostname; + } + + @Override + protected void execute(final IMonitoringRecord element) throws Exception { + if (element instanceof OperationCallEvent) { + final OperationCallEvent operation = (OperationCallEvent) element; + this.outputPort.send(new TraceMetadata(this.traceId++, 0, "", this.hostname, 0, 0)); + this.outputPort.send(new BeforeOperationEvent(this.now++, this.traceId, 0, operation.getSourceOperation(), + operation.getSourceComponent())); + this.outputPort.send(new BeforeOperationEvent(this.now++, this.traceId, 1, operation.getTargetOperation(), + operation.getTargetComponent())); + this.outputPort.send(new AfterOperationEvent(this.now++, this.traceId, 2, operation.getTargetOperation(), + operation.getTargetComponent())); + this.outputPort.send(new AfterOperationEvent(this.now++, this.traceId, 3, operation.getSourceOperation(), + operation.getSourceComponent())); + } else if (element instanceof IFlowRecord) { + this.outputPort.send((IFlowRecord) element); + } + } + +} diff --git a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/TeetimeConfiguration.java b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/TeetimeConfiguration.java index 8493fda5a9d220ad54391eedfed9c358788fc9e8..f2d59cfb20954bb11b9fe6152d17576b0de9e014 100644 --- a/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/TeetimeConfiguration.java +++ b/tools/create-architecture-model/src/main/java/org/oceandsl/architecture/model/TeetimeConfiguration.java @@ -4,19 +4,40 @@ package org.oceandsl.architecture.model; import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; import org.oceandsl.analysis.RewriteBeforeAndAfterEventsStage; +import kieker.analysis.graph.IGraph; +import kieker.analysis.graph.dependency.AssemblyLevelComponentDependencyGraphBuilderFactory; +import kieker.analysis.graph.dependency.AssemblyLevelOperationDependencyGraphBuilderFactory; +import kieker.analysis.graph.dependency.DependencyGraphCreatorStage; +import kieker.analysis.graph.export.dot.DotFileWriterStage; +import kieker.analysis.graph.export.graphml.GraphMLFileWriterStage; +import kieker.analysis.graph.util.FileExtension; +import kieker.analysis.model.AssemblyModelAssemblerStage; import kieker.analysis.model.DeploymentModelAssemblerStage; -import kieker.analysisteetime.model.analysismodel.assembly.AssemblyFactory; +import kieker.analysis.model.TypeModelAssemblerStage; +import kieker.analysis.signature.IComponentSignatureExtractor; +import kieker.analysis.signature.IOperationSignatureExtractor; +import kieker.analysis.signature.NameBuilder; +import kieker.analysis.statistics.StatisticsModel; +import kieker.analysis.util.stage.trigger.Trigger; +import kieker.analysis.util.stage.trigger.TriggerOnTerminationStage; import kieker.analysisteetime.model.analysismodel.assembly.AssemblyModel; -import kieker.analysisteetime.model.analysismodel.deployment.DeploymentFactory; import kieker.analysisteetime.model.analysismodel.deployment.DeploymentModel; +import kieker.analysisteetime.model.analysismodel.execution.ExecutionModel; +import kieker.analysisteetime.model.analysismodel.type.ComponentType; +import kieker.analysisteetime.model.analysismodel.type.OperationType; +import kieker.analysisteetime.model.analysismodel.type.TypeModel; import kieker.common.record.IMonitoringRecord; import kieker.common.record.flow.IFlowRecord; import kieker.tools.source.LogsReaderCompositeStage; import teetime.framework.Configuration; import teetime.stage.InstanceOfFilter; +import teetime.stage.basic.distributor.Distributor; +import teetime.stage.basic.distributor.strategy.CopyByReferenceStrategy; /** * @author reiner @@ -24,7 +45,9 @@ import teetime.stage.InstanceOfFilter; */ public class TeetimeConfiguration extends Configuration { - public TeetimeConfiguration(final ArchitectureModelSettings parameterConfiguration) throws IOException { + public TeetimeConfiguration(final ArchitectureModelSettings parameterConfiguration, final TypeModel typeModel, + final AssemblyModel assemblyModel, final DeploymentModel deploymentModel, + final ExecutionModel executionModel, final StatisticsModel statisticsModel) throws IOException { final kieker.common.configuration.Configuration configuration = new kieker.common.configuration.Configuration(); configuration.setProperty(LogsReaderCompositeStage.LOG_DIRECTORIES, @@ -34,20 +57,102 @@ public class TeetimeConfiguration extends Configuration { final RewriteBeforeAndAfterEventsStage processor = new RewriteBeforeAndAfterEventsStage( parameterConfiguration.getAddrlineExecutable(), parameterConfiguration.getModelExecutable()); + + final ProduceBeforeAndAfterEventsFromOperationCalls produceEvents = new ProduceBeforeAndAfterEventsFromOperationCalls( + "localhost"); + final InstanceOfFilter<IMonitoringRecord, IFlowRecord> instanceOfFilter = new InstanceOfFilter<>( IFlowRecord.class); - // final CreateCallsStage callsStage = new CreateCallsStage(); - // final CountUniqueCalls countStage = new CountUniqueCalls(); + final CountEvents<IFlowRecord> counter = new CountEvents<>(1000000); - final AssemblyModel assemblyModel = AssemblyFactory.eINSTANCE.createAssemblyModel(); - final DeploymentModel deploymentModel = DeploymentFactory.eINSTANCE.createDeploymentModel(); + + final IComponentSignatureExtractor componentSignatureExtractor = new IComponentSignatureExtractor() { + + @Override + public void extract(final ComponentType componentType) { + String signature = componentType.getSignature(); + final String prefix = parameterConfiguration.getPrefix(); + if (signature == null) { + signature = "-- none --"; + } else if (signature.startsWith(prefix)) { + signature = signature.substring(prefix.length()); + } + final Path path = Paths.get(signature); + final String name = path.getName(path.getNameCount() - 1).toString(); + final String rest = (path.getParent() != null) ? path.getParent().toString() : "<<unknown>>"; + componentType.setName(name); + componentType.setPackage(rest); + } + }; + final IOperationSignatureExtractor operationSignatureExtractor = new IOperationSignatureExtractor() { + + @Override + public void extract(final OperationType operationType) { + final String name = operationType.getSignature(); + operationType.setName(name); + operationType.setReturnType("unknown"); + } + + }; + final TypeModelAssemblerStage typeModelAssemblerStage = new TypeModelAssemblerStage(typeModel, + componentSignatureExtractor, operationSignatureExtractor); + final AssemblyModelAssemblerStage assemblyModelAssemblerStage = new AssemblyModelAssemblerStage(typeModel, + assemblyModel); final DeploymentModelAssemblerStage deploymentModelAssemblerStage = new DeploymentModelAssemblerStage( assemblyModel, deploymentModel); + final CreateCallsStage callsStage = new CreateCallsStage(deploymentModel); + final ExecutionModelGenerationStage executionModelGenerationStage = new ExecutionModelGenerationStage( + executionModel); + final CountUniqueCalls countUniqueCalls = new CountUniqueCalls(statisticsModel, executionModel); + + final TriggerOnTerminationStage triggerOnTerminationStage = new TriggerOnTerminationStage(); + + final Distributor<Trigger> distributor = new Distributor<>(new CopyByReferenceStrategy()); + + final DependencyGraphCreatorStage operationDependencyGraphCreatorStage = new DependencyGraphCreatorStage( + executionModel, statisticsModel, new AssemblyLevelOperationDependencyGraphBuilderFactory()); + final DotFileWriterStage dotFileOperationDependencyWriterStage = new DotFileWriterStage( + new DedicatedFileNameMapper(parameterConfiguration.getOutputFile().getPath(), "operation", + FileExtension.DOT), + new DotExportConfigurationFactory(NameBuilder.forJavaShortOperations()) + .createForAssemblyLevelOperationDependencyGraph(false)); + + final Distributor<IGraph> distributorGraphs = new Distributor<>(new CopyByReferenceStrategy()); + + final DependencyGraphCreatorStage componentDependencyGraphCreatorStage = new DependencyGraphCreatorStage( + executionModel, statisticsModel, new AssemblyLevelComponentDependencyGraphBuilderFactory()); + final DotFileWriterStage dotFileComponentDependencyWriterStage = new DotFileWriterStage( + new DedicatedFileNameMapper(parameterConfiguration.getOutputFile().getPath(), "component", + FileExtension.DOT), + new DotExportConfigurationFactory(NameBuilder.forJavaShortOperations()) + .createForAssemblyLevelComponentDependencyGraph(false)); + + final GraphMLFileWriterStage graphMLFileWriterStage = new GraphMLFileWriterStage( + parameterConfiguration.getOutputFile().getPath()); + this.connectPorts(reader.getOutputPort(), processor.getInputPort()); - this.connectPorts(processor.getOutputPort(), instanceOfFilter.getInputPort()); + this.connectPorts(processor.getOutputPort(), produceEvents.getInputPort()); + this.connectPorts(produceEvents.getOutputPort(), instanceOfFilter.getInputPort()); this.connectPorts(instanceOfFilter.getMatchedOutputPort(), counter.getInputPort()); - this.connectPorts(counter.getOutputPort(), deploymentModelAssemblerStage.getInputPort()); + this.connectPorts(counter.getOutputPort(), typeModelAssemblerStage.getInputPort()); + this.connectPorts(typeModelAssemblerStage.getOutputPort(), assemblyModelAssemblerStage.getInputPort()); + this.connectPorts(assemblyModelAssemblerStage.getOutputPort(), deploymentModelAssemblerStage.getInputPort()); + this.connectPorts(deploymentModelAssemblerStage.getOutputPort(), callsStage.getInputPort()); + this.connectPorts(callsStage.getOutputPort(), executionModelGenerationStage.getInputPort()); + this.connectPorts(executionModelGenerationStage.getOutputPort(), countUniqueCalls.getInputPort()); + this.connectPorts(countUniqueCalls.getOutputPort(), triggerOnTerminationStage.getInputPort()); + this.connectPorts(triggerOnTerminationStage.getOutputPort(), distributor.getInputPort()); + + this.connectPorts(distributor.getNewOutputPort(), operationDependencyGraphCreatorStage.getInputPort()); + this.connectPorts(distributor.getNewOutputPort(), componentDependencyGraphCreatorStage.getInputPort()); + + this.connectPorts(operationDependencyGraphCreatorStage.getOutputPort(), distributorGraphs.getInputPort()); + this.connectPorts(distributorGraphs.getNewOutputPort(), dotFileOperationDependencyWriterStage.getInputPort()); + this.connectPorts(distributorGraphs.getNewOutputPort(), graphMLFileWriterStage.getInputPort()); + + this.connectPorts(componentDependencyGraphCreatorStage.getOutputPort(), + dotFileComponentDependencyWriterStage.getInputPort()); } }