Skip to content

Commit

Permalink
Add custom control for filtering string lists
Browse files Browse the repository at this point in the history
  • Loading branch information
patschuh committed Mar 30, 2019
1 parent f29915e commit eb6f9bb
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 122 deletions.
2 changes: 1 addition & 1 deletion README.md
Expand Up @@ -23,5 +23,5 @@ Mesages displayed in the message list can be exportet in csv format and played i
Allows for defining and configurating topics once and apply them to different clusters with one file, see the [Wiki]("https://github.com/patschuh/KafkaEsque/wiki/Topic-Templates") for Details
***
### Message Books
Allows for playing a set of Messages over different topics into a cluster, see [Wiki]("https://github.com/patschuh/KafkaEsque/wiki/Message-Books") for details
Allows for playing a set of Messages over different topics into a cluster, see the [Wiki]("https://github.com/patschuh/KafkaEsque/wiki/Message-Books") for details
***
23 changes: 6 additions & 17 deletions src/main/java/at/esque/kafka/Controller.java
Expand Up @@ -7,6 +7,7 @@
import at.esque.kafka.cluster.ClusterConfig;
import at.esque.kafka.cluster.KafkaesqueAdminClient;
import at.esque.kafka.cluster.TopicMessageTypeConfig;
import at.esque.kafka.controls.FilterableListView;
import at.esque.kafka.controls.JsonTreeView;
import at.esque.kafka.dialogs.ClusterConfigDialog;
import at.esque.kafka.dialogs.DeleteClustersDialog;
Expand Down Expand Up @@ -160,12 +161,10 @@ public class Controller {
@FXML
private TableColumn<KafkaMessage, String> messageTimestampColumn;
@FXML
private ListView<String> topicListView;
private FilterableListView topicListView;
@FXML
private ComboBox<ClusterConfig> clusterComboBox;
@FXML
private Button refreshTopicListButton;
@FXML
private MenuItem playMessageBookMenu;
@FXML
private ComboBox<FetchTypes> fetchModeCombobox;
Expand All @@ -186,8 +185,6 @@ public class Controller {
@FXML
private Label taskProgressLabel;
@FXML
private TextField topicFilterTextField;
@FXML
private TextField messageSearchTextField;
@FXML
private Button interruptMessagePollingButton;
Expand All @@ -198,7 +195,7 @@ public class Controller {
private YAMLMapper yamlMapper = new YAMLMapper();

private String selectedTopic() {
return topicListView.getSelectionModel().getSelectedItem();
return topicListView.getListView().getSelectionModel().getSelectedItem();
}


Expand Down Expand Up @@ -264,7 +261,7 @@ public void setup(Stage controlledStage) {
refreshTopicList(newValue);
});

topicListView.setCellFactory(lv -> topicListCellFactory());
topicListView.getListView().setCellFactory(lv -> topicListCellFactory());

messageSearchTextField.textProperty().addListener((observable, oldValue, newValue) -> filteredMessages.setPredicate(km -> (km.getKey() != null && StringUtils.containsIgnoreCase(km.getKey(), newValue) || (km.getValue() != null && StringUtils.containsIgnoreCase(km.getValue(), newValue)))));

Expand All @@ -281,9 +278,6 @@ public void setup(Stage controlledStage) {
setupClusterCombobox();
clusterComboBox.setItems(configHandler.loadOrCreateConfigs().getClusterConfigs());

topicFilterTextField.textProperty().addListener(((observable, oldValue, newValue) ->
((FilteredList<String>) topicListView.getItems()).setPredicate(t -> StringUtils.containsIgnoreCase(t, newValue))
));
jsonTreeView.jsonStringProperty().bind(valueTextArea.textProperty());
jsonTreeView.visibleProperty().bind(formatJsonToggle.selectedProperty());
bindDisableProperties();
Expand Down Expand Up @@ -313,7 +307,6 @@ private void bindDisableProperties() {
publishMessageButton.disableProperty().bind(backgroundTaskInProgressProperty);
clusterComboBox.disableProperty().bind(backgroundTaskInProgressProperty);
playMessageBookMenu.disableProperty().bind(backgroundTaskInProgressProperty);
refreshTopicListButton.disableProperty().bind(backgroundTaskInProgressProperty);
editClusterButton.disableProperty().bind(clusterComboBox.getSelectionModel().selectedItemProperty().isNull());
}

Expand Down Expand Up @@ -435,8 +428,6 @@ private ListCell<String> topicListCellFactory() {
}

private void refreshTopicList(ClusterConfig newValue) {
Platform.runLater(() -> topicListView.getItems().clear());

backGroundTaskHolder.setBackGroundTaskDescription("getting Topics...");
runInDaemonThread(() -> getTopicsForCluster(newValue));
}
Expand All @@ -447,9 +438,7 @@ private void getTopicsForCluster(ClusterConfig clusterConfig) {
stopWatch.start();
LOGGER.info("Started getting topics for cluster");
backGroundTaskHolder.setIsInProgress(true);
ObservableList<String> topics = FXCollections.observableArrayList(adminClient.getTopics());
FilteredList<String> filteredTopics = new FilteredList<>(topics.sorted(), t -> true);
Platform.runLater(() -> topicListView.setItems(filteredTopics));
Platform.runLater(() -> topicListView.setItems(adminClient.getTopics()));
} finally {
stopWatch.stop();
LOGGER.info("Finished getting topics for cluster [{}]", stopWatch);
Expand Down Expand Up @@ -983,7 +972,7 @@ public void playMessageBook(ActionEvent event) {
List<KafkaMessagBookWrapper> messagesToSend = new ArrayList<>();
Platform.runLater(() -> backGroundTaskHolder.setBackGroundTaskDescription("Playing Message Book: scanning messages"));
listedFiles.forEach(file -> {
if (!topicListView.getItems().contains(file.getName())) {
if (!topicListView.getBaseList().contains(file.getName())) {
throw new RuntimeException(String.format("No such topic [%s] in current cluster", file.getName()));
}
addMessagesToSend(messagesToSend, file);
Expand Down
13 changes: 7 additions & 6 deletions src/main/java/at/esque/kafka/CrossClusterController.java
Expand Up @@ -6,6 +6,7 @@
import at.esque.kafka.cluster.CrossClusterOperation;
import at.esque.kafka.cluster.KafkaesqueAdminClient;
import at.esque.kafka.cluster.TopicMessageTypeConfig;
import at.esque.kafka.controls.FilterableListView;
import at.esque.kafka.handlers.ConfigHandler;
import at.esque.kafka.handlers.ConsumerHandler;
import at.esque.kafka.handlers.CrossClusterOperationHandler;
Expand Down Expand Up @@ -52,9 +53,9 @@ public class CrossClusterController {
private TextField amountLimit;

@FXML
private ListView<String> fromClusterTopicsList;
private FilterableListView fromClusterTopicsList;
@FXML
private ListView<String> toClusterTopicsList;
private FilterableListView toClusterTopicsList;
@FXML
private ListView<CrossClusterOperation> runningOperationsList;
@FXML
Expand Down Expand Up @@ -123,15 +124,15 @@ public void setup() {
refreshOperationList(null);
}

private void setupClusterControls(ClusterConfig clusterConfig, KafkaesqueAdminClient adminClient, ListView topicList) {
private void setupClusterControls(ClusterConfig clusterConfig, KafkaesqueAdminClient adminClient, FilterableListView topicList) {
if (adminClient != null) {
adminClient.close();
}
adminClient = new KafkaesqueAdminClient(clusterConfig.getBootStrapServers());
KafkaesqueAdminClient finalAdminClient = adminClient;
runInDaemonThread(() -> {
ObservableList<String> topics = FXCollections.observableArrayList(finalAdminClient.getTopics());
Platform.runLater(() -> topicList.setItems(topics.sorted()));
Platform.runLater(() -> topicList.setItems(topics));
});
}

Expand Down Expand Up @@ -198,8 +199,8 @@ public void startOperationClick(ActionEvent actionEvent) {
ClusterConfig fromCluster = fromClusterComboBox.getSelectionModel().getSelectedItem();
ClusterConfig toCluster = toClusterComboBox.getSelectionModel().getSelectedItem();

String fromTopicName = fromClusterTopicsList.getSelectionModel().getSelectedItem();
String toTopicName = toClusterTopicsList.getSelectionModel().getSelectedItem();
String fromTopicName = fromClusterTopicsList.getListView().getSelectionModel().getSelectedItem();
String toTopicName = toClusterTopicsList.getListView().getSelectionModel().getSelectedItem();

TopicMessageTypeConfig fromTopic = configHandler.getConfigForTopic(fromCluster.getIdentifier(), fromTopicName);
TopicMessageTypeConfig toTopic = configHandler.getConfigForTopic(toCluster.getIdentifier(), toTopicName);
Expand Down
@@ -1,20 +1,24 @@
package at.esque.kafka;

import at.esque.kafka.alerts.ErrorAlert;
import at.esque.kafka.controls.FilterableListView;
import at.esque.kafka.controls.JsonTreeView;
import io.confluent.kafka.schemaregistry.client.rest.RestService;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.ComboBox;
import javafx.scene.control.ListView;
import javafx.scene.control.TextArea;

import java.util.Arrays;


public class SchemaRegistryBrowserController {

private RestService schemaRegistryRestService;
@FXML
private ListView<String> subjectListView;
private FilterableListView subjectListView;
@FXML
private TextArea schemaTextArea;
@FXML
Expand All @@ -27,17 +31,17 @@ public void setup(String schemaregistryUrl) {
jsonTreeView.jsonStringProperty().bind(schemaTextArea.textProperty());
try {
versionComboBox.getSelectionModel().selectedItemProperty().addListener(((observable1, oldValue1, newValue1) -> {
if(newValue1 == null){
if (newValue1 == null) {
schemaTextArea.setText(null);
return;
}
try {
schemaTextArea.setText(JsonUtils.formatJson(schemaRegistryRestService.getVersion(subjectListView.getSelectionModel().getSelectedItem(), newValue1).getSchema()));
schemaTextArea.setText(JsonUtils.formatJson(schemaRegistryRestService.getVersion(subjectListView.getListView().getSelectionModel().getSelectedItem(), newValue1).getSchema()));
} catch (Exception e) {
ErrorAlert.show(e);
}
}));
subjectListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
subjectListView.getListView().getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> {
try {
versionComboBox.setItems(FXCollections.observableArrayList(schemaRegistryRestService.getAllVersions(newValue)));
if (versionComboBox.getItems().size() > 0) {
Expand Down
162 changes: 162 additions & 0 deletions src/main/java/at/esque/kafka/controls/FilterableListView.java
@@ -0,0 +1,162 @@
package at.esque.kafka.controls;

import javafx.beans.binding.Bindings;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.collections.transformation.SortedList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.util.Collection;

public class FilterableListView extends VBox {
@FXML
private TextField filtertextField;
@FXML
private ListView<String> listView;
@FXML
private HBox toolbarBox;
@FXML
private Button addButton;
@FXML
private Button refreshButton;

private ObservableList<String> baseList;
private FilteredList<String> filteredList;
private SortedList<String> sortedList;

public BooleanProperty addButtonVisible = new SimpleBooleanProperty(true);
public BooleanProperty refreshButtonVisible = new SimpleBooleanProperty(true);

public FilterableListView() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(
"/fxml/controls/filterableListView.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
setup();
}

private void setup() {
baseList = FXCollections.observableArrayList();
filteredList = new FilteredList<>(baseList);
sortedList = new SortedList<>(filteredList);
listView.setItems(sortedList);

filtertextField.textProperty().addListener((observable, oldValue, newValue) -> {
filteredList.setPredicate(item -> StringUtils.isEmpty(newValue)
|| StringUtils.containsIgnoreCase(item, newValue));
});
sortedList.setComparator(String::compareTo);

bindButtonProperties();
}

private void bindButtonProperties() {
addButton.onActionProperty().bind(onAddActionProperty());
addButton.visibleProperty().bind(addButtonVisibleProperty());
refreshButton.visibleProperty().bind(refreshButtonVisibleProperty());
addButton.maxWidthProperty().bind(Bindings.when(addButtonVisibleProperty()).then(Region.USE_COMPUTED_SIZE).otherwise(0));
addButton.minWidthProperty().bind(Bindings.when(addButtonVisibleProperty()).then(Region.USE_COMPUTED_SIZE).otherwise(0));
refreshButton.maxWidthProperty().bind(Bindings.when(refreshButtonVisibleProperty()).then(Region.USE_COMPUTED_SIZE).otherwise(0));
refreshButton.minWidthProperty().bind(Bindings.when(refreshButtonVisibleProperty()).then(Region.USE_COMPUTED_SIZE).otherwise(0));
}

public ObservableList<String> getBaseList() {
return baseList;
}

public void setItems(Collection<String> items) {
baseList.clear();
baseList.addAll(items);
}

public void addItems(Collection<String> items) {
baseList.addAll(items);
}

public ListView<String> getListView() {
return listView;
}

public boolean isAddButtonVisible() {
return addButtonVisible.get();
}

public BooleanProperty addButtonVisibleProperty() {
return addButtonVisible;
}

public void setAddButtonVisible(boolean addButtonVisible) {
this.addButtonVisible.set(addButtonVisible);
}

public boolean isRefreshButtonVisible() {
return refreshButtonVisible.get();
}

public BooleanProperty refreshButtonVisibleProperty() {
return refreshButtonVisible;
}

public void setRefreshButtonVisible(boolean refreshButtonVisible) {
this.refreshButtonVisible.set(refreshButtonVisible);
}

public final ObjectProperty<EventHandler<ActionEvent>> onAddActionProperty() { return onAddAction; }
public final void setOnAddAction(EventHandler<ActionEvent> value) { onAddActionProperty().set(value); }
public final EventHandler<ActionEvent> getOnAddAction() { return onAddActionProperty().get(); }
private ObjectProperty<EventHandler<ActionEvent>> onAddAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
@Override protected void invalidated() {
setEventHandler(ActionEvent.ACTION, get());
}

@Override
public Object getBean() {
return FilterableListView.this;
}

@Override
public String getName() {
return "onAddAction";
}
};

public final ObjectProperty<EventHandler<ActionEvent>> onRefreshActionProperty() { return onRefreshAction; }
public final void setOnRefreshAction(EventHandler<ActionEvent> value) { onRefreshActionProperty().set(value); }
public final EventHandler<ActionEvent> getOnRefreshAction() { return onRefreshActionProperty().get(); }
private ObjectProperty<EventHandler<ActionEvent>> onRefreshAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {
@Override protected void invalidated() {
setEventHandler(ActionEvent.ACTION, get());
}

@Override
public Object getBean() {
return FilterableListView.this;
}

@Override
public String getName() {
return "onRefreshAction";
}
};
}

0 comments on commit eb6f9bb

Please sign in to comment.