JavaFX8 TableView не се актуализира с извикване на списък от RestTemplate в планирана услуга

Аз съм сравнително нов в Java и JavaFX и разработвам приложение JavaFX и използвам REST api в задача javafx.concurrent.ScheduledService, за да актуализирам TableView. Мога да получа отговора от моята услуга SpringBoot REST и мога да видя, че данните се задават в моделния обект, ObservableList, както и самия TableView, но таблицата все още остава празна.

Крайна точка на API на SpringBoot:

 @RequestMapping(value = "/getAllActive", method = RequestMethod.GET)
public List<IssuedTicket> getAllActive () {
    List<IssuedTicket> issuedTicketList = issuedTicketService.findAll();
    return issuedTicketList;
}

JSON отговор от горната крайна точка:

[{"id":2,"ticketId":1230717013545,"dateArrived":"23-07-17","timeArrived":"01:35:45","deviceId":1},{"id":3,"ticketId":1230717013552,"dateArrived":"23-07-17","timeArrived":"01:35:52","deviceId":1},{"id":4,"ticketId":1230717013556,"dateArrived":"23-07-17","timeArrived":"01:35:56","deviceId":1}]

В моето JavaFX приложение:

HomeSceneController.java

package com.ronintech.bayTrans.ui.main;

import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXTextField;
import com.ronintech.bayTrans.model.ActiveTickets;
import com.ronintech.bayTrans.utils.RestErrorHandler;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.util.Duration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.net.URL;
import java.util.List;
import java.util.ResourceBundle;

public class HomeSceneController implements Initializable{

@FXML
private AnchorPane homeAnchorPane;

@FXML
private JFXTextField ticketTxt;

@FXML
private JFXButton scanTicketBtn;

@FXML
private TableView<ActiveTickets> activeTicketsTable;

@FXML
private TableColumn<ActiveTickets, Long > ticketIdCol;

@FXML
private TableColumn<ActiveTickets, String > dateArrivedCol;

@FXML
private TableColumn<ActiveTickets, String> timeArrivedCol;

private ObservableList<ActiveTickets> activeTicketsList;

private static final Logger LOGGER = LoggerFactory.getLogger(HomeSceneController.class);
private static final String ACTIVE_TICKETS_URL = "http://localhost:9090/api/ticket/getAllActive";

@Override
public void initialize(URL location, ResourceBundle resources) {
    getActiveTickets.reset();
    getActiveTickets.setPeriod(Duration.seconds(20));
    getActiveTickets.start();

    getActiveTickets.setOnSucceeded(event -> {
        LOGGER.info("onSuccess");
        List<ActiveTickets> activeList = getActiveTickets.getValue();
        activeTicketsList = FXCollections.observableArrayList(activeList);

        ticketIdCol.setCellValueFactory(new PropertyValueFactory<ActiveTickets, Long>("ticketId"));
        dateArrivedCol.setCellValueFactory(new PropertyValueFactory<ActiveTickets, String>("dateArrived"));
        timeArrivedCol.setCellValueFactory(new PropertyValueFactory<ActiveTickets, String>("timeArrived"));
        activeTicketsTable.setItems(activeTicketsList);

        LOGGER.info("Items in Table");
        LOGGER.info(activeTicketsTable.getItems().toString());
    });

    getActiveTickets.setOnFailed(event -> {
        LOGGER.error("service task FAILED");
    });
}

@FXML
void openTicketModal(ActionEvent event) {
}

private ScheduledService<List<ActiveTickets>> getActiveTickets = new ScheduledService<List<ActiveTickets>>() {
    @Override
    protected Task<List<ActiveTickets>> createTask() {
        return new Task<List<ActiveTickets>>() {
            @Override
            protected List<ActiveTickets> call() throws Exception {
                LOGGER.info("Scheduled Service getActiveTickets STARTED");
                RestTemplate restTemplate = new RestTemplate();
                restTemplate.setErrorHandler(new RestErrorHandler());

                List<ActiveTickets> response = restTemplate.getForObject(ACTIVE_TICKETS_URL, List.class);

                LOGGER.info(response.toString);
                return response;
            }
        };
    }
};

}

Моят моделен обект: ActiveTickets.java

package com.ronintech.bayTrans.model;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleStringProperty;

@JsonIgnoreProperties(ignoreUnknown = true)
public class ActiveTickets {

private SimpleLongProperty id;
private SimpleLongProperty ticketId;
private SimpleStringProperty dateArrived;
private SimpleStringProperty timeArrived;
private SimpleIntegerProperty deviceId;

public ActiveTickets(long id,
                     long ticketId,
                     String dateArrived,
                     String timeArrived,
                     int deviceId) {
    this.id = new SimpleLongProperty(id);
    this.ticketId = new SimpleLongProperty(ticketId);
    this.dateArrived = new SimpleStringProperty(dateArrived);
    this.timeArrived = new SimpleStringProperty(timeArrived);
    this.deviceId = new SimpleIntegerProperty(deviceId);
}

public long getId() {
    return id.get();
}

public SimpleLongProperty idProperty() {
    return id;
}

public long getTicketId() {
    return ticketId.get();
}

public SimpleLongProperty ticketIdProperty() {
    return ticketId;
}

public String getDateArrived() {
    return dateArrived.get();
}

public SimpleStringProperty dateArrivedProperty() {
    return dateArrived;
}

public String getTimeArrived() {
    return timeArrived.get();
}

public SimpleStringProperty timeArrivedProperty() {
    return timeArrived;
}

public int getDeviceId() {
    return deviceId.get();
}

public SimpleIntegerProperty deviceIdProperty() {
    return deviceId;
}

public void setId(long id) {
    this.id.set(id);
}

public void setTicketId(long ticketId) {
    this.ticketId.set(ticketId);
}

public void setDateArrived(String dateArrived) {
    this.dateArrived.set(dateArrived);
}

public void setTimeArrived(String timeArrived) {
    this.timeArrived.set(timeArrived);
}

public void setDeviceId(int deviceId) {
    this.deviceId.set(deviceId);
}

@Override
public String toString() {
    return "ActiveTickets{" +
            "ticketId=" + ticketId +
            ", dateArrived=" + dateArrived +
            ", timeArrived=" + timeArrived +
            '}';
}
}

Отговорът от извикването на RestTemplate е:

00:43:06.901 [Thread-9] INFO com.ronintech.bayTrans.ui.main.HomeSceneController - [{id=2, ticketId=1230717013545, dateArrived=23-07-17, timeArrived=01:35:45, deviceId=1}, {id=3, ticketId=1230717013552, dateArrived=23-07-17, timeArrived=01:35:52, deviceId=1}, {id=4, ticketId=1230717013556, dateArrived=23-07-17, timeArrived=01:35:56, deviceId=1}]

Дневникът след задаване на ObservableList на TableView:

activeTicketsTable.setItems(activeTicketsList);

00:43:07.159 [JavaFX Application Thread] INFO com.ronintech.bayTrans.ui.main.HomeSceneController - [{id=2, ticketId=1230717013545, dateArrived=23-07-17, timeArrived=01:35:45, deviceId=1}, {id=3, ticketId=1230717013552, dateArrived=23-07-17, timeArrived=01:35:52, deviceId=1}, {id=4, ticketId=1230717013556, dateArrived=23-07-17, timeArrived=01:35:56, deviceId=1}]

Регистрационният файл показва, че данните за TableView се задават в основната нишка на потребителския интерфейс, но таблицата все още е празна

TableView

Не съм сигурен дали и какво правя погрешно. Моля, помогнете, ако някой знае как да реши това.

Благодаря предварително.

РЕДАКТИРАНЕ

Моята структура на TableView в FXML:

<TableView fx:id="activeTicketsTable" prefHeight="800.0" prefWidth="800.0" tableMenuButtonVisible="true">
    <columns>
        <TableColumn fx:id="ticketIdCol" prefWidth="75.0" text="Ticket ID"/>
        <TableColumn fx:id="dateArrivedCol" prefWidth="75.0" text="Date Arrived"/>
        <TableColumn fx:id="timeArrivedCol" prefWidth="75.0" text="Time Arrived"/>
    </columns>
    <columnResizePolicy>
        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY"/>
    </columnResizePolicy>
</TableView>

АКТУАЛИЗАЦИЯ

Изглежда маршалингът не се извършва правилно.

Това е трасирането за отстраняване на грешки на ObservableList activeTicketsList:

activeTicketsList

Списъкът е зададен като ArrayList на LinkedHashMaps'

Докато когато правя ръчно ObservableList:

private final ObservableList<ActiveTickets> data = FXCollections.observableArrayList(
        new ActiveTickets(1,1234,"date1","time1",1),
        new ActiveTickets(2,5678,"date2","time2",2)
);

"данни" е правилно зададен като ArrayList от обекти от клас ActiveTickets

данни

Как мога да се уверя, че JSON -> Маршалирането на обекти се извършва правилно в RestTemplate?


person VRN.dev    schedule 23.07.2017    source източник
comment
Екранната снимка показва, че действително има елементи в таблицата (празна таблица има контейнер на мястото на редовете). Така че всички регистрационни файлове и екранни снимки са в съответствие с фабричната стойност на клетката, която не е зададена в колоните. Тези, които показвате в кода, изглеждат правилни; най-доброто ми предположение тук би било, че по някакъв начин екземплярите на колоната на таблицата, на които извиквате setCellValueFactory(...), не са правилните екземпляри на колоната на таблицата.   -  person James_D    schedule 24.07.2017
comment
Да, таблицата се настройва със списъка, предоставен от услугата, но изглежда, че маршалингът на JSON-›Object се обърка. Някакви съвети как да поправя това?   -  person VRN.dev    schedule 24.07.2017
comment
Вижте дали stackoverflow.com/questions/8108887/ помага. Вашият ActiveTickets клас почти сигурно също се нуждае от конструктор без аргументи.   -  person James_D    schedule 24.07.2017
comment
Това е точно като това, което каза James_D, добавете конструктор без аргументи и използвайте или ParameterizedTypeReference, или просто marshal за масив вместо списък. Трябва да направите и двете стъпки, за да работи това.   -  person Jai    schedule 25.07.2017


Отговори (1)


Не съм сигурен дали това всъщност е проблемът, но като цяло моите REST клиентски проекти не се маршалират директно към списък (може да е проблемът на Джаксън). Ето какво бих направил:

ActiveTickets[] response = restTemplate.getForObject(ACTIVE_TICKETS_URL, ActiveTickets[].class);

return Arrays.asList(response);

Обърнете внимание, че пак ще върнете списък в крайната точка; не е необходимо да променяте това.

Актуализация

Прочетете това за друг начин да направите това.

person Jai    schedule 24.07.2017
comment
Опитах това, но услугата преминава в състояние FAILED. 11:45:51.658 [Нишка на JavaFX приложение] ГРЕШКА com.ronintech.bayTrans.ui.main.HomeSceneController - НЕУСПЕШНА сервизна задача. Но съм съгласен с предпоставката, трябва да има някакъв проблем в маршалинга от JSON -› Object - person VRN.dev; 24.07.2017
comment
@VRN.dev stacktrace казва ли нещо за това къде и какво се е объркало? - person Jai; 24.07.2017
comment
@VRN.dev btw вашият ActiveTickets вероятно се нуждае от празен конструктор по подразбиране. - person Jai; 24.07.2017