I’ve built a simple app for displaying data (using Grid) that I retrieve via API calls, here is a flow I have:
Delete all existing data
@Scheduled (default spring) method in service to execute each 15 minutes that fetches some data via API and stores in h2 database
On UI a have several elements (e.g. TextField, ComboBox) that have values I take for sorting the grid (it’s connected to the entity and executes SQL query via repository)
Is that possible to somehow update the view from this @Scheduled method after it’s finished? (or probably I’d like to display some PregrossBar while it’s in progress ) I am aware of UI.getCurrent().access() but looks like it should be called only from UI thread, so it’s not an option in my case. I can make this update by clicking button on the page, but anyway looking for some solution with usage of @Scheduled
Service:
@Service
public class DataService {
private final DataRepository dataRepository;
public DataService(DataRepository dataRepository) {
this.dataRepository = dataRepository;
}
public List<DataEntity> findAll() {
return dataRepository.search();
}
@Scheduled(cron = "0 */15 * * * *")
private void fetchData() {
// Delete data
// fetch data
// Update UI?? homeView#filterDataGrid
}
}
View:
@Push
@Route("")
public class HomeView extends VerticalLayout {
private final Grid<DataEntity> dataEntityGrid = new Grid<>(DataEntity.class);
private final DataService dataService;
public HomeView(DataService dataService) {
this.dataService = dataService;
setSizeFull();
add(dataEntityGrid);
}
private void filterDataGrid() {
dataEntityGrid.setItems(dataService.findAll());
}
}
getUI() is not thread safe. You should call it e.g. in an attach handler or user event handler when the background job is set up. That will give you an UI reference that you can use to call ui.access later from the background thread.
Which UI do you want to update? @Scheduled creates a “global” task that will be run independent of any UIs. One approach you could use is to have a list of listeners that should be notified when the data changes
private final List<Consumer<MyData>> dataListeners = new CopyOnWriteArrayList<>();
@Scheduled(cron = "0 */15 * * * *")
private void fetchData() {
MyData data = fetchData();
dataListeners.forEach(listener -> listener.accept(data));
}
Each view would then add a callback to that list when it’s attached and remove itself when detached.
I just want to call homeView#filterDataGrid (dataEntityGrid.setItems(dataService.findAll());) from @Scheduled service method. I’ll try to implement something according to documentation you’ve shared, thanks. Looks a bit complicated especially for not developers
What you need with something like @Scheduled is an application level EventBus. Fire event from your scheduled task and observe it in the view (or preferably presenter of your view).
Have you considered using starting a Timer from your UI code instead of @Scheduled? That would resolve the issue of needing to locate an UI instance to update. Granted, it is probably not the most thread-efficient way of doing things, so if that’s a concern, you should go for Tatu’s suggestion instead.