From 53b6a4b4f0aa3470a02b63065ddb150c308f05ea Mon Sep 17 00:00:00 2001 From: Christopher Zell Date: Mon, 10 Jun 2024 20:30:42 +0200 Subject: [PATCH 1/5] build: move slf4j version --- backend/pom.xml | 1 - pom.xml | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/pom.xml b/backend/pom.xml index 585e02b..9a55f62 100644 --- a/backend/pom.xml +++ b/backend/pom.xml @@ -42,7 +42,6 @@ 2.22.2 UTF-8 3.6.3 - 2.0.13 4.2.1 2.13.0 2.13.0 diff --git a/pom.xml b/pom.xml index 5938369..df5e043 100644 --- a/pom.xml +++ b/pom.xml @@ -56,6 +56,7 @@ 21 3.3.1 2.23 + 2.0.13 4.5 UTF-8 30.0-jre From bf5522914d979ff3f1b5f71792649c7503b87c9c Mon Sep 17 00:00:00 2001 From: Christopher Zell Date: Mon, 10 Jun 2024 20:31:33 +0200 Subject: [PATCH 2/5] feat: add zdb frontend * Adds a new module that contains a frontend for zdb * The frontend allows to * observe the log, each record property is an own column * investigate the state, via selecting different CF's. The state view shows keys in correct formats, and values as strings. --- frontend/pom.xml | 136 +++++++++ .../zell/zdb/frontend/LogViewController.java | 126 ++++++++ .../zdb/frontend/StateViewController.java | 161 ++++++++++ .../io/zell/zdb/frontend/ZdbApplication.java | 37 +++ .../io/zell/zdb/frontend/ZdbFrontendMain.java | 23 ++ .../io/zell/zdb/frontend/ZeebeRecord.java | 285 ++++++++++++++++++ .../io/zell/zdb/frontend/log-view.fxml | 21 ++ .../io/zell/zdb/frontend/state-view.fxml | 23 ++ .../io/zell/zdb/frontend/zdb-view.fxml | 15 + pom.xml | 1 + 10 files changed, 828 insertions(+) create mode 100644 frontend/pom.xml create mode 100644 frontend/src/main/java/io/zell/zdb/frontend/LogViewController.java create mode 100644 frontend/src/main/java/io/zell/zdb/frontend/StateViewController.java create mode 100644 frontend/src/main/java/io/zell/zdb/frontend/ZdbApplication.java create mode 100644 frontend/src/main/java/io/zell/zdb/frontend/ZdbFrontendMain.java create mode 100644 frontend/src/main/java/io/zell/zdb/frontend/ZeebeRecord.java create mode 100644 frontend/src/main/resources/io/zell/zdb/frontend/log-view.fxml create mode 100644 frontend/src/main/resources/io/zell/zdb/frontend/state-view.fxml create mode 100644 frontend/src/main/resources/io/zell/zdb/frontend/zdb-view.fxml diff --git a/frontend/pom.xml b/frontend/pom.xml new file mode 100644 index 0000000..180e2c2 --- /dev/null +++ b/frontend/pom.xml @@ -0,0 +1,136 @@ + + + 4.0.0 + + + + io.zell + zdb + 2.4.0-SNAPSHOT + ../pom.xml + + + frontend + frontend + + + UTF-8 + 5.10.0 + + + + + org.openjfx + javafx-controls + 21 + + + org.openjfx + javafx-fxml + 21 + + + + org.junit.jupiter + junit-jupiter-api + ${junit.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit.version} + test + + + io.zell + backend + 2.4.0-SNAPSHOT + compile + + + + org.slf4j + slf4j-simple + ${version.slf4j} + test + + + + + + + + + com.spotify.fmt + fmt-maven-plugin + ${plugin.version.fmt} + + + validate + + format + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + + org.openjfx + javafx-maven-plugin + 0.0.8 + + + + default-cli + + io.zell.zdb.frontend.ZdbFrontendMain + app + app + app + true + true + true + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + 3.7.1 + + + make-assembly + package + + single + + + + + io.zell.zdb.frontend.ZdbFrontendMain + true + + + + jar-with-dependencies + + + + + + + + \ No newline at end of file diff --git a/frontend/src/main/java/io/zell/zdb/frontend/LogViewController.java b/frontend/src/main/java/io/zell/zdb/frontend/LogViewController.java new file mode 100644 index 0000000..02bf9b0 --- /dev/null +++ b/frontend/src/main/java/io/zell/zdb/frontend/LogViewController.java @@ -0,0 +1,126 @@ +/* + * Copyright © 2021 Christopher Kujawa (zelldon91@gmail.com) + * + * 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 io.zell.zdb.frontend; + +import io.zell.zdb.log.LogContentReader; +import io.zell.zdb.log.records.ApplicationRecord; +import io.zell.zdb.log.records.PersistedRecord; +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.ResourceBundle; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.DirectoryChooser; + +public class LogViewController implements Initializable { + + @FXML private TableView zeebeData; + + @FXML private Button findDataPath; + + @FXML private TextField dataPath; + private final DirectoryChooser directoryChooser = new DirectoryChooser(); + private ObservableList dataObservableList; + + @Override + public void initialize(final URL url, final ResourceBundle resourceBundle) { + // time to initialize fields + final var userHome = System.getProperty("user.home"); + this.dataPath.setText(userHome); + + this.dataObservableList = FXCollections.observableList(new ArrayList<>()); + // put into the data table view + this.zeebeData.setItems(this.dataObservableList); + + // define columns + this.zeebeData + .getColumns() + .setAll( + LogViewController.createTableColumn("Position", "position"), + LogViewController.createTableColumn( + "Source\nRecord\nPosition", "sourceRecordPosition"), + LogViewController.createTableColumn("Timestamp", "timestamp"), + LogViewController.createTableColumn("Key", "key"), + LogViewController.createTableColumn("Record\ntype", "recordType"), + LogViewController.createTableColumn("Value\ntype", "valueType"), + LogViewController.createTableColumn("Intent", "intent"), + LogViewController.createTableColumn("Rejection\ntype", "rejectionType"), + LogViewController.createTableColumn("Rejection\nreason", "rejectionReason"), + LogViewController.createTableColumn("Request\nID", "requestId"), + LogViewController.createTableColumn("Request\nstream\nID", "requestStreamId"), + LogViewController.createTableColumn("Protocol\nversion", "protocolVersion"), + LogViewController.createTableColumn("Broker\nversion", "brokerVersion"), + LogViewController.createTableColumn("Record\nversion", "recordVersion"), + LogViewController.createTableColumn("Auth\ndata", "authData"), + LogViewController.createTableColumn("Record\nvalue", "recordValue")); + } + + private static TableColumn createTableColumn( + final String displayName, final String propertyName) { + final TableColumn column = new TableColumn(displayName); + column.setCellValueFactory(new PropertyValueFactory(propertyName)); + return column; + } + + @FXML + protected void onFindFile() { + this.directoryChooser.setTitle("Zeebe data path"); + this.directoryChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + + final File file = this.directoryChooser.showDialog(this.findDataPath.getScene().getWindow()); + + if (file != null) { + this.dataPath.setText(file.getAbsolutePath()); + + final var logContentReader = new LogContentReader(new File(this.dataPath.getText()).toPath()); + while (logContentReader.hasNext()) { + final PersistedRecord next = logContentReader.next(); + + if (next instanceof final ApplicationRecord applicationRecord) { + for (final var r : applicationRecord.getEntries()) { + final var zeebeRecord = + new ZeebeRecord( + r.getPosition(), + r.getSourceRecordPosition(), + r.getTimestamp(), + r.getKey(), + r.getRecordType().name(), + r.getValueType().name(), + r.getIntent().name(), + r.getRejectionType().name(), + r.getRejectionReason(), + r.getRequestId(), + r.getRequestStreamId(), + r.getProtocolVersion(), + r.getBrokerVersion(), + r.getRecordVersion(), + r.getAuthData(), + r.getRecordValue().toString()); + this.dataObservableList.add(zeebeRecord); + } + } + } + } + } +} diff --git a/frontend/src/main/java/io/zell/zdb/frontend/StateViewController.java b/frontend/src/main/java/io/zell/zdb/frontend/StateViewController.java new file mode 100644 index 0000000..9bda0a7 --- /dev/null +++ b/frontend/src/main/java/io/zell/zdb/frontend/StateViewController.java @@ -0,0 +1,161 @@ +/* + * Copyright © 2021 Christopher Kujawa (zelldon91@gmail.com) + * + * 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 io.zell.zdb.frontend; + +import io.camunda.zeebe.protocol.ZbColumnFamilies; +import io.zell.zdb.state.KeyFormatters; +import io.zell.zdb.state.ZeebeDbReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.ResourceBundle; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.DirectoryChooser; + +public class StateViewController implements Initializable { + + @FXML private TableView zeebeData; + + @FXML private Button findDataPath; + + @FXML private TextField dataPath; + + @FXML private ChoiceBox columnFamily; + + private final DirectoryChooser directoryChooser = new DirectoryChooser(); + private ObservableList dataObservableList; + + @Override + public void initialize(final URL url, final ResourceBundle resourceBundle) { + // time to initialize fields + final var userHome = System.getProperty("user.home"); + this.dataPath.setText(userHome); + + this.columnFamily.setItems( + FXCollections.observableList( + Arrays.stream(ZbColumnFamilies.values()).map(Enum::name).toList())); + this.columnFamily.setValue(ZbColumnFamilies.DEFAULT.name()); + + // put into the data table view + this.dataObservableList = FXCollections.observableList(new ArrayList<>()); + this.zeebeData.setItems(this.dataObservableList); + // define columns + this.zeebeData.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + this.zeebeData + .getColumns() + .setAll( + StateViewController.createTableColumn("Key", "key"), + StateViewController.createTableColumn("Value", "value")); + } + + private static TableColumn createTableColumn( + final String displayName, final String propertyName) { + final TableColumn column = new TableColumn(displayName); + column.setCellValueFactory(new PropertyValueFactory(propertyName)); + return column; + } + + public class StateDataKV { + private final StringProperty key; + private final StringProperty value; + + public StateDataKV(final String key, final String value) { + this.key = new SimpleStringProperty(key); + this.value = new SimpleStringProperty(value); + } + + public String getKey() { + return this.key.get(); + } + + public StringProperty keyProperty() { + return this.key; + } + + public void setKey(final String key) { + this.key.set(key); + } + + public String getValue() { + return this.value.get(); + } + + public StringProperty valueProperty() { + return this.value; + } + + public void setValue(final String value) { + this.value.set(value); + } + } + + @FXML + protected void onFindFile() { + this.directoryChooser.setTitle("Zeebe data path"); + this.directoryChooser.setInitialDirectory(new File(System.getProperty("user.home"))); + + final File file = this.directoryChooser.showDialog(this.findDataPath.getScene().getWindow()); + + if (file != null) { + this.dataPath.setText(file.getAbsolutePath()); + fillTableViewWithStateData(this.dataPath.getText(), this.columnFamily.getValue()); + } + } + + private void fillTableViewWithStateData(final String path, final String columnFamilyName) { + // read data based on column family + final var zeebeDbReader = new ZeebeDbReader(new File(path).toPath()); + final var columnFamilyValue = ZbColumnFamilies.valueOf(columnFamilyName); + + final var keyFormatter = KeyFormatters.ofDefault().forColumnFamily(columnFamilyValue); + + // remove old content + this.dataObservableList.clear(); + this.zeebeData.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); + // update table view + zeebeDbReader.visitDBWithPrefix( + columnFamilyValue, + (key, value) -> + this.dataObservableList.add(new StateDataKV(keyFormatter.formatKey(key), value))); + } + + public void selectColumnFamily() { + // do something on selecting CF + try { + final var expectedPath = this.dataPath.getText(); + + fillTableViewWithStateData(expectedPath, this.columnFamily.getValue()); + } catch (final Exception exception) { + if (exception instanceof FileNotFoundException) { + // no state path + System.out.println( + String.format( + "Current path %s doesn't point to a state path.", this.dataPath.getText())); + return; + } + exception.printStackTrace(); + } + } +} diff --git a/frontend/src/main/java/io/zell/zdb/frontend/ZdbApplication.java b/frontend/src/main/java/io/zell/zdb/frontend/ZdbApplication.java new file mode 100644 index 0000000..93131af --- /dev/null +++ b/frontend/src/main/java/io/zell/zdb/frontend/ZdbApplication.java @@ -0,0 +1,37 @@ +/* + * Copyright © 2021 Christopher Kujawa (zelldon91@gmail.com) + * + * 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 io.zell.zdb.frontend; + +import java.io.IOException; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.stage.Stage; + +public class ZdbApplication extends Application { + @Override + public void start(final Stage stage) throws IOException { + final FXMLLoader fxmlLoader = new FXMLLoader(ZdbApplication.class.getResource("zdb-view.fxml")); + final Scene scene = new Scene(fxmlLoader.load(), 320, 240); + stage.setTitle("Zeebe debug and inspection tool"); + stage.setScene(scene); + stage.show(); + } + + public static void main(final String[] args) { + launch(); + } +} diff --git a/frontend/src/main/java/io/zell/zdb/frontend/ZdbFrontendMain.java b/frontend/src/main/java/io/zell/zdb/frontend/ZdbFrontendMain.java new file mode 100644 index 0000000..cf6149f --- /dev/null +++ b/frontend/src/main/java/io/zell/zdb/frontend/ZdbFrontendMain.java @@ -0,0 +1,23 @@ +/* + * Copyright © 2021 Christopher Kujawa (zelldon91@gmail.com) + * + * 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 io.zell.zdb.frontend; + +public class ZdbFrontendMain { + + public static void main(String[] args) { + ZdbApplication.main(args); + } +} diff --git a/frontend/src/main/java/io/zell/zdb/frontend/ZeebeRecord.java b/frontend/src/main/java/io/zell/zdb/frontend/ZeebeRecord.java new file mode 100644 index 0000000..6d4411c --- /dev/null +++ b/frontend/src/main/java/io/zell/zdb/frontend/ZeebeRecord.java @@ -0,0 +1,285 @@ +/* + * Copyright © 2021 Christopher Kujawa (zelldon91@gmail.com) + * + * 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 io.zell.zdb.frontend; + +import javafx.beans.property.*; + +public class ZeebeRecord { + // Record(val position: Long, + // val sourceRecordPosition: Long, + // val timestamp: Long, + // val key: Long, + // val recordType: RecordType, + // val valueType: ValueType, + // @Serializable(with = IntentSerializer::class) + // val intent: Intent, + // val rejectionType: RejectionType? = RejectionType.NULL_VAL, + // val rejectionReason: String? = "", + // val requestId: Long? = 0, + // val requestStreamId: Int = 0, + // val protocolVersion: Int, + // val brokerVersion: String, + // val recordVersion: Int ? = 0, + // val authData: String ? = "", + // val recordValue: JsonElement, + // /*Transient marks to ignore the property during serialization */ + // @Transient val piRelatedValue: ProcessInstanceRelatedValue? = + // null + private final LongProperty position; + private final LongProperty sourceRecordPosition; + private final LongProperty timestamp; + private final LongProperty key; + private final StringProperty recordType; + private final StringProperty ValueType; + private final StringProperty intent; + + private final StringProperty rejectionType; + private final StringProperty rejectionReason; + private final LongProperty requestId; + private final IntegerProperty requestStreamId; + private final IntegerProperty protocolVersion; + private final StringProperty brokerVersion; + private final IntegerProperty recordVersion; + private final StringProperty authData; + private final StringProperty recordValue; + + public ZeebeRecord( + final long position, + final long sourceRecordPostion, + final long timestamp, + final long key, + final String recordType, + final String valueType, + final String intent, + final String rejectionType, + final String rejectionReason, + final long requestId, + final int requestStreamId, + final int protocolVersion, + final String brokerVersion, + final int recordVersion, + final String authData, + final String recordValue) { + this.position = new SimpleLongProperty(position); + this.sourceRecordPosition = new SimpleLongProperty(sourceRecordPostion); + this.timestamp = new SimpleLongProperty(timestamp); + this.key = new SimpleLongProperty(key); + this.recordType = new SimpleStringProperty(recordType); + this.ValueType = new SimpleStringProperty(valueType); + this.intent = new SimpleStringProperty(intent); + this.rejectionType = new SimpleStringProperty(rejectionType); + this.rejectionReason = new SimpleStringProperty(rejectionReason); + this.requestId = new SimpleLongProperty(requestId); + this.requestStreamId = new SimpleIntegerProperty(requestStreamId); + this.protocolVersion = new SimpleIntegerProperty(protocolVersion); + this.brokerVersion = new SimpleStringProperty(brokerVersion); + this.recordVersion = new SimpleIntegerProperty(recordVersion); + this.authData = new SimpleStringProperty(authData); + this.recordValue = new SimpleStringProperty(recordValue); + } + + public String getRejectionType() { + return this.rejectionType.get(); + } + + public StringProperty rejectionTypeProperty() { + return this.rejectionType; + } + + public void setRejectionType(final String rejectionType) { + this.rejectionType.set(rejectionType); + } + + public String getRejectionReason() { + return this.rejectionReason.get(); + } + + public StringProperty rejectionReasonProperty() { + return this.rejectionReason; + } + + public void setRejectionReason(final String rejectionReason) { + this.rejectionReason.set(rejectionReason); + } + + public long getRequestId() { + return this.requestId.get(); + } + + public LongProperty requestIdProperty() { + return this.requestId; + } + + public void setRequestId(final long requestId) { + this.requestId.set(requestId); + } + + public int getRequestStreamId() { + return this.requestStreamId.get(); + } + + public IntegerProperty requestStreamIdProperty() { + return this.requestStreamId; + } + + public void setRequestStreamId(final int requestStreamId) { + this.requestStreamId.set(requestStreamId); + } + + public int getProtocolVersion() { + return this.protocolVersion.get(); + } + + public IntegerProperty protocolVersionProperty() { + return this.protocolVersion; + } + + public void setProtocolVersion(final int protocolVersion) { + this.protocolVersion.set(protocolVersion); + } + + public String getBrokerVersion() { + return this.brokerVersion.get(); + } + + public StringProperty brokerVersionProperty() { + return this.brokerVersion; + } + + public void setBrokerVersion(final String brokerVersion) { + this.brokerVersion.set(brokerVersion); + } + + public int getRecordVersion() { + return this.recordVersion.get(); + } + + public IntegerProperty recordVersionProperty() { + return this.recordVersion; + } + + public void setRecordVersion(final int recordVersion) { + this.recordVersion.set(recordVersion); + } + + public String getAuthData() { + return this.authData.get(); + } + + public StringProperty authDataProperty() { + return this.authData; + } + + public void setAuthData(final String authData) { + this.authData.set(authData); + } + + public String getRecordValue() { + return this.recordValue.get(); + } + + public StringProperty recordValueProperty() { + return this.recordValue; + } + + public void setRecordValue(final String recordValue) { + this.recordValue.set(recordValue); + } + + public long getPosition() { + return this.position.get(); + } + + public LongProperty positionProperty() { + return this.position; + } + + public void setPosition(final long position) { + this.position.set(position); + } + + public long getSourceRecordPosition() { + return this.sourceRecordPosition.get(); + } + + public LongProperty sourceRecordPositionProperty() { + return this.sourceRecordPosition; + } + + public void setSourceRecordPosition(final long sourceRecordPosition) { + this.sourceRecordPosition.set(sourceRecordPosition); + } + + public long getTimestamp() { + return this.timestamp.get(); + } + + public LongProperty timestampProperty() { + return this.timestamp; + } + + public void setTimestamp(final long timestamp) { + this.timestamp.set(timestamp); + } + + public long getKey() { + return this.key.get(); + } + + public LongProperty keyProperty() { + return this.key; + } + + public void setKey(final long key) { + this.key.set(key); + } + + public String getRecordType() { + return this.recordType.get(); + } + + public StringProperty recordTypeProperty() { + return this.recordType; + } + + public void setRecordType(final String recordType) { + this.recordType.set(recordType); + } + + public String getValueType() { + return this.ValueType.get(); + } + + public StringProperty valueTypeProperty() { + return this.ValueType; + } + + public void setValueType(final String valueType) { + this.ValueType.set(valueType); + } + + public String getIntent() { + return this.intent.get(); + } + + public StringProperty intentProperty() { + return this.intent; + } + + public void setIntent(final String intent) { + this.intent.set(intent); + } +} diff --git a/frontend/src/main/resources/io/zell/zdb/frontend/log-view.fxml b/frontend/src/main/resources/io/zell/zdb/frontend/log-view.fxml new file mode 100644 index 0000000..720d2de --- /dev/null +++ b/frontend/src/main/resources/io/zell/zdb/frontend/log-view.fxml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + +