-1

I am developing booking system. It will have 2 main users. When user1 request booking for a vehicle, user2 will receive a popup notification stating that user1 is requesting a booking for a vehicle.

When user1 sends a vehicle booking request, the request data should be inserted in the database. In user2 controller, user2 should check/read the database and if there is new request received then there should be an automatic popup notification.

Below is my code for user2 controller to read the database and prompt a popup notification when new record exist.

I am using the TrayNotification. But it didn't popup when the app is currently open and running. However, it only popup when user login to the user2 account, after that when new bookings were made it never popup.

User2 Code:

//-------------------------- get Notifications --------------------------//
    public void getNotification() throws ClassNotFoundException, SQLException{
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql://localhost/vbms_db";
        conn =(Connection) DriverManager.getConnection(url,"root","");
        System.out.println("Connected");
        try { 
            getDepartment= lbl_holdUserDepartment.getText();
            pst = conn.prepareStatement("SELECT * FROM bookings WHERE driver_department='"+getDepartment+"' AND vehicle_AvailStatus = 'Pending...'");
            rs = pst.executeQuery();
            if(rs.next()==true){
                NotificationType nt = NotificationType.WARNING;
                TrayNotification tray = new TrayNotification();
                tray.setTitle("Notification");
                tray.setMessage("Vehicle Booking Requested.");
                tray.showAndDismiss(Duration.millis(3000));
                tray.setNotificationType(nt);         
            }
        } catch (SQLException exception) {
            System.out.println(exception);
        }
    }

Really need help.

Abra
  • 19,142
  • 7
  • 29
  • 41
T_Miles
  • 1
  • 2
  • Yes, I am using the tray notification. But it didn't popup when the app is currently open and running. However, it only popup when user login to the user2 account, after that when new bookings were made it never popup. – T_Miles Sep 02 '23 at 02:59
  • 1
    Note that `Class.forName()` is [no longer required](https://stackoverflow.com/questions/33391496/is-class-forname-mechanism-needed). Also, if you are using [PreparedStatement](https://docs.oracle.com/javase/tutorial/jdbc/basics/prepared.html) then you should use a parameter. The `if` condition can be simply [if (rs.next())](https://java.by-comparison.com/). In the `catch` block, better to print the [stack trace](https://docs.oracle.com/javase/tutorial/essential/exceptions/index.html). I suggest that you try to post a [mcve]. – Abra Sep 02 '23 at 03:01
  • I would suggest you separate your DB logic. With the limited info you offered, my guess is that you have to use a `Task` or `Service` to run a query on the DB every 30 seconds to 5 minutes. Make a duration that is good for you. I would use 1 to 5 seconds in the testing phase. – SedJ601 Sep 02 '23 at 06:25
  • Here is me thinking this through for the testing phase. I would query the DB every five seconds in the background by using something from [JavafX concurrent package](https://stackoverflow.com/a/60685975/2423906). If I get results, I would pause the background ScheduledService and use something from the Animation API to display the tray notifications one after another. After the tray notifications finish, I would restart the ScheduledService. I have never attempted something like this. The experts might have better advice. – SedJ601 Sep 02 '23 at 06:35

1 Answers1

2

I could not get TrayNotification to work with the latest version of JavaFX. I used ControlsFX Notification. The ideas should be very similar.

Use ScheduledService to periodically check new db notifications. If ScheduledService finds new notifications, stop ScheduledService from looking for new notifications. Display the current notifications using Timeline. Restart the ScheduledService once the Timeline finishes displaying the current notifications.

Main

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.util.Duration;
import org.controlsfx.control.Notifications;

public class App extends Application {
    
    static List<MyNotification> myNotifications = new ArrayList();
    static FakeDatabaseHandler fakeDatabaseHandler = new FakeDatabaseHandler();
    
    @Override
    public void start(Stage stage) {
        
        Button btnGenerateNotification = new Button("Generate!");
        btnGenerateNotification.setOnAction(actionEvent ->{            
             fakeDatabaseHandler.addFakeNotificationsToDb();
        });

        Label lblUpdateMessage = new Label("Update Message: ");
        
        ScheduledService<List<MyNotification>> checkForScheduledService = new CheckForNotificationsScheduledService();
        checkForScheduledService.setPeriod(Duration.seconds(5));
        checkForScheduledService.messageProperty().addListener((ov, oldValue, newValue) -> {
            if(newValue != null)
            {
                lblUpdateMessage.setText("Update Message: " + newValue);
            }
        });
        
        checkForScheduledService.valueProperty().addListener((ov, oldValue, newValue) -> {
            if(newValue != null && !newValue.isEmpty())
            {
                myNotifications.clear();
                myNotifications.addAll(newValue);
                checkForScheduledService.cancel();
                
                AtomicInteger index = new AtomicInteger(0);
                checkForScheduledService.cancel();
                Timeline threeSecondsWonder = new Timeline(
                    new KeyFrame(Duration.seconds(3),  event -> {    
                        MyNotification myNotification = myNotifications.get(index.getAndIncrement());
                        Notifications.create().title(myNotification.getDepartment() + " - " + myNotification.getTitle()).text(myNotification.getMessage()).hideAfter(Duration.seconds(3)).showWarning();
                    })
                );
                threeSecondsWonder.setCycleCount(myNotifications.size());
                threeSecondsWonder.play();
                threeSecondsWonder.setOnFinished((t) -> {
                    checkForScheduledService.restart();
                });
            }
        });
        checkForScheduledService.start();
        
        StackPane root = new StackPane(new VBox(btnGenerateNotification, lblUpdateMessage));

        stage.setScene(new Scene(root, 500, 500));
        stage.setTitle("Facilities");
        stage.show();        
    }

    public static void main(String[] args) {
        launch(args);
    }
    
    private static class CheckForNotificationsScheduledService extends ScheduledService<List<MyNotification>> {

    @Override
    protected Task<List<MyNotification>> createTask() {
        return new Task<List<MyNotification>>() {
            @Override
            protected List<MyNotification> call() throws Exception {
                List<MyNotification> fakeNotificationList = fakeDatabaseHandler.getFakeNotificationsFromDB();
                System.out.println("Checking for notifications. Notifications Found: " + fakeNotificationList.size());
                updateMessage("Checking for notifications. Notifications Found: " + fakeNotificationList.size());
                updateValue(fakeNotificationList);                
                
                return fakeNotificationList; 
            }
        };
    }
  }
}

MyNotification

/**
 *
 * @author Sed
 */
public class MyNotification {
    private String department;
    private String title;
    private String message;

    public MyNotification(String department, String title, String message) {
        this.department = department;
        this.title = title;
        this.message = message;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("MyNotification{");
        sb.append("department=").append(department);
        sb.append(", title=").append(title);
        sb.append(", message=").append(message);
        sb.append('}');
        return sb.toString();
    }
}

FakeDatabaseHandler

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;

/**
 *
 * @author Sed
 */
public class FakeDatabaseHandler {
    private final List<MyNotification> fakeDBData = new ArrayList();
    
    //private Connection connection;
    
    public FakeDatabaseHandler() {
        //Estiblish the connection here
        /*
         try {
            connection = DriverManager.getConnection("jdbc:sqlite:vbms_db.db");
            System.out.println("Connected to SQLite Db: vbms_db.db");
        }
        catch (SQLException ex) {
            System.out.println(ex.toString());
        }
        */
    }
    
    public void addFakeNotificationsToDb(){
        List<String> departmentList = Arrays.asList("A", "B", "C", "D");
        //Use a PreparedStatement here! I am not gonig to demo that!      
        
        //Generating Fake DB data!
        
        
        for(int i = 0; i < ThreadLocalRandom.current().nextInt(1, 6); i++)
        {
            int notificationID = ThreadLocalRandom.current().nextInt( 1000000);
            fakeDBData.add(new MyNotification("Department: " + departmentList.get(ThreadLocalRandom.current().nextInt(4)), "Title " + notificationID, "Message: " + notificationID));
        }
    }
    
    public List<MyNotification> getFakeNotificationsFromDB(){
        List<MyNotification> returnList = new ArrayList(fakeDBData);
        fakeDBData.clear();
        
        return returnList;
    }
}

Module-Info

module sed.home.simpletestjavafx {
    requires javafx.controls;
    requires org.controlsfx.controls;
    
    exports sed.home.simpletestjavafx;
}

Output

enter image description here

SedJ601
  • 12,173
  • 3
  • 41
  • 59