I am having a problem trying to figure out how to make a TableView
show the correct data based on each entry response status. I thought FilteredList
would get the job done but it's not. Basically, I am checking URLs and getting their status codes. I am using a FilteredList
to show all URLs that are pending, that was successful, etc. If I change the ChoiceBox
from All
to Pending
, the FilteredList
does show only pending URLs, but as the URLs change to Success
or something else the FilteredList
does not filter them out the current view. What should happen is when I change to Pending
, any URLs that receives a status change should drop from the current view. How do I get the FilteredList/TableView
to do real-time updates?
Main
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* @author blj0011
*/
public class JavaFXApplication240 extends Application
{
@Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args)
{
launch(args);
}
}
Controller
import com.sun.javafx.application.HostServicesDelegate;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;
import java.util.Scanner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.LaxRedirectStrategy;
/**
*
* @author blj0011
*/
public class FXMLDocumentController implements Initializable
{
@FXML
private Button btnProcess;
@FXML
private TableView<Model> tvMain;
@FXML
private TableColumn<Model, String> tcBibId, tcHoldingId, tcUrl, tcStatus;
@FXML
private TableColumn<Model, Integer> tcId;
@FXML
private ChoiceBox<String> cbMain;
private final ObservableList<Model> masterData = FXCollections.observableArrayList();
FilteredList<Model> filteredData;
HostServicesDelegate hostServicesDelegate;
AtomicInteger processUrlCounter;
int tableViewSize = -1;
AtomicInteger seconds = new AtomicInteger();
@Override
public void initialize(URL url, ResourceBundle rb)
{
// TODO
processUrlCounter = new AtomicInteger();
setupChoiceBox();
setupTableView();
btnProcess.setOnAction((event) -> {
btnProcess.setDisable(true);
List<Task<String>> tasks = new ArrayList();
tvMain.getItems().forEach((t) -> {
Model tempModel = (Model) t;
tasks.add(processUrl(tempModel));
});
tasks.forEach(exec::execute);
});
}
private List<Model> getTestData()
{
List<Model> testList = new ArrayList();
Random random = new Random();
try (Scanner input = new Scanner(new File("testdata.txt"))) {
while (input.hasNext()) {
Model tempModel = new Model(Integer.toString(random.nextInt(100000) + 100000), Integer.toString(random.nextInt(100000) + 100000), input.next());
testList.add(tempModel);
System.out.println(tempModel.toString());
}
}
catch (FileNotFoundException ex) {
Logger.getLogger(FXMLDocumentController.class.getName()).log(Level.SEVERE, null, ex);
}
return testList;
}
private void setupChoiceBox()
{
List<String> STATUS_LIST = Arrays.asList("ALL", "PENDING", "SUCCESS");
cbMain.getItems().addAll(STATUS_LIST);
cbMain.getSelectionModel().selectFirst();
cbMain.getSelectionModel().selectedItemProperty().addListener((obs, oldVal, newVal)
-> {
List<Model> tempList = new ArrayList();
switch (newVal) {
case "ALL":
tempList = tvMain.getItems();
filteredData.setPredicate(null);
break;
case "PENDING":
filteredData.setPredicate((t) -> {
return t.getStatus().trim().equals("PENDING");
});
tempList = tvMain.getItems().filtered((t) -> {
return t.getStatus().equals("PENDING");
});
break;
case "SUCCESS":
filteredData.setPredicate((t) -> {
return t.getStatus().trim().matches("2\d\d");
});
tempList = tvMain.getItems().filtered((t) -> {
return t.getStatus().matches("2\d\d");
});
break;
}
});
}
private void setupTableView()
{
tcId.setCellValueFactory(new PropertyValueFactory("id"));
tcBibId.setCellValueFactory(cell -> cell.getValue().bibIdProperty());
tcHoldingId.setCellValueFactory(cell -> cell.getValue().holdingIdProperty());
tcUrl.setCellValueFactory(cell -> cell.getValue().urlProperty());
tcStatus.setCellValueFactory(cell -> cell.getValue().statusProperty());
masterData.setAll(getTestData());
filteredData = new FilteredList(masterData, null);
tvMain.setItems(filteredData);
tableViewSize = masterData.size();
}
private final ExecutorService exec = Executors.newFixedThreadPool(2500, r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
private Task<String> processUrl(Model model)
{
Task<String> task = new Task<String>()
{
@Override
protected String call() throws Exception
{
CloseableHttpClient httpclient = HttpClients.custom().setDefaultRequestConfig(RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build()).setRedirectStrategy(new LaxRedirectStrategy()).build();
HttpGet httpGet = new HttpGet(model.getUrl());
try (CloseableHttpResponse response1 = httpclient.execute(httpGet)) {
String tempResponse = response1.getStatusLine().toString().split(" ")[1];
return tempResponse;
}
catch (IOException ex) {
String tempString = ex.getClass().toString().split(" ")[1];
String[] tempException = tempString.split("\.");
return tempException[tempException.length - 1];
}
}
};
task.setOnSucceeded((event) -> {
model.setStatus(task.getValue());
if (processUrlCounter.incrementAndGet() == tableViewSize) {
btnProcess.setDisable(false);
}
String tempOutput = "Processed URLs: " + processUrlCounter.get() + " of " + tableViewSize;
});
return task;
}
}
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.layout.VBox?>
<VBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="862.0" prefWidth="748.0" spacing="10.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="javafxapplication240.FXMLDocumentController">
<children>
<ChoiceBox fx:id="cbMain" prefWidth="150.0" />
<TableView fx:id="tvMain" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
<columns>
<TableColumn fx:id="tcId" prefWidth="85.0" text="ID" />
<TableColumn fx:id="tcBibId" prefWidth="102.0" text="Bib ID" />
<TableColumn fx:id="tcHoldingId" prefWidth="113.0" text="Holding ID" />
<TableColumn fx:id="tcUrl" prefWidth="313.0" text="URL" />
<TableColumn fx:id="tcStatus" prefWidth="132.0" text="Status" />
</columns>
</TableView>
<Button fx:id="btnProcess" mnemonicParsing="false" text="Process" />
</children>
<padding>
<Insets bottom="10.0" top="10.0" />
</padding>
</VBox>
Model Class
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
/**
*
* @author Sedrick
*/
public class Model
{
final private IntegerProperty id = new SimpleIntegerProperty();
final private StringProperty bibId = new SimpleStringProperty();
final private StringProperty holdingId = new SimpleStringProperty();
final private StringProperty url = new SimpleStringProperty();
final private StringProperty status = new SimpleStringProperty();
public Model(int id, String bibId, String holdingID, String url)
{
this.id.set(id);
this.bibId.set(bibId);
this.holdingId.set(holdingID);
this.url.set(url);
this.status.set("PENDING");
}
public Model(String bibId, String holdingId, String url)
{
this.id.set(-1);
this.bibId.set(bibId);
this.holdingId.set(holdingId);
this.url.set(url);
this.status.set("PENDING");
}
public IntegerProperty idProperty()
{
return this.id;
}
public StringProperty bibIdProperty()
{
return this.bibId;
}
public StringProperty holdingIdProperty()
{
return this.holdingId;
}
public StringProperty urlProperty()
{
return this.url;
}
public StringProperty statusProperty()
{
return this.status;
}
public void se
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…