We've been developing a chat, we use javafx for the interface, the problem is when we recieve a message from the server appears this error:
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
at com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:206)
at guichat.Procesos.createBubble(Procesos.java:232)
at guichat.Procesos.MostrarMensajeAmigo(Procesos.java:204)
at guichat.Procesos.MensajeRecibido(Procesos.java:192)
at guichat.Procesos.mensajeria(Procesos.java:177)
at guichat.HomeController.run(HomeController.java:285)
at java.lang.Thread.run(Thread.java:748)
We've been looking at posts with the same problem, we know that we can't update a JavaFX element the way we're doing it because it's a different thread.
This is my class Procesos:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package guichat;
import com.google.gson.Gson;
import guichat.Modelos.Amigo;
import guichat.Modelos.Usuario;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
import guichat.Modelos.Comunicacion;
import guichat.Modelos.Mensaje;
import guichat.Modelos.MensajeGrupo;
import javafx.scene.layout.VBox;
/**
*
* @author usuario
*/
public class Procesos {
public static String ip;
public static int puerto;
public static Socket soquet;
public static VBox mensajes;
public static Gson json = new Gson();
public Procesos()
{
}
public static String getIp() {
return ip;
}
public static void setIp(String ip) {
Procesos.ip = ip;
}
public static int getPuerto() {
return puerto;
}
public static void setPuerto(int puerto) {
Procesos.puerto = puerto;
}
public static void CrearSocket(String ip, int puerto)
{
try {
soquet= new Socket(ip,puerto);
} catch (IOException ex) {
Logger.getLogger(Procesos.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static double Login(String usuario, String contraseña, String servidor){
String ip= servidor;
Usuario user = new Usuario();
Comunicacion modeloInput = new Comunicacion();
Comunicacion modeloOutput = new Comunicacion();
try {
Procesos.CrearSocket(ip, 4567);
DataOutputStream dataOutput=new DataOutputStream(Procesos.soquet.getOutputStream());
user.setUsername(usuario);
user.setPassword(contraseña);
modeloOutput.setTipo(Comunicacion.MTypes.RQ_LOGIN);
modeloOutput.setContenido(user);
dataOutput.writeUTF(json.toJson(modeloOutput));
DataInputStream dataInput= new DataInputStream(Procesos.soquet.getInputStream());
modeloInput= json.fromJson(dataInput.readUTF(), Comunicacion.class);
if (modeloInput.getTipo()== Comunicacion.MTypes.ACK_LOGIN) {
System.out.println(modeloInput.getContenido());
if ((double)modeloInput.getContenido()==210.0) {
return (double)modeloInput.getContenido();
}
}
} catch (IOException I) {
I.getMessage();
return 0;
}
return 0;
}
public static double Register(String nick, String contraseña, String servidor){
System.out.println("Hola");
Usuario usuario = new Usuario();
Comunicacion modeloRespuesta = new Comunicacion();
Comunicacion modeloPeticion = new Comunicacion();
try {
System.out.println("Entre");
Procesos.CrearSocket(servidor, 4567);
DataOutputStream peticion = new DataOutputStream(Procesos.soquet.getOutputStream());
usuario.setUsername(nick);
usuario.setPassword(contraseña);
modeloPeticion.setTipo(Comunicacion.MTypes.RQ_REG);
modeloPeticion.setContenido(usuario);
peticion.writeUTF(json.toJson(modeloPeticion));
DataInputStream respuesta = new DataInputStream(Procesos.soquet.getInputStream());
modeloRespuesta= json.fromJson(respuesta.readUTF(), Comunicacion.class);
if (modeloRespuesta.getTipo()== Comunicacion.MTypes.ACK) {
System.out.println(modeloRespuesta.getContenido());
if ((double)modeloRespuesta.getContenido()==220.0) {
return (double)modeloRespuesta.getContenido();
}
}
} catch (IOException I) {
I.getMessage();
return 0;
}
return 0;
}
public static void EnviarMensajes(String txtMessage)
{
DataOutputStream EnviarCadena = null;
Usuario origen = new Usuario();
origen.setId(1);
System.out.println("Enviando mensaje");
try {
Comunicacion modeloOutput = new Comunicacion();
System.out.println(txtMessage);
Mensaje mensaje_enviar= new Mensaje();
Usuario usuario_destino = new Usuario();
usuario_destino.setId(2);
mensaje_enviar.setDestino(usuario_destino);
mensaje_enviar.setOrigen(origen);
mensaje_enviar.setContenido(txtMessage);
modeloOutput.setTipo(Comunicacion.MTypes.RQ_MENSAJE);
modeloOutput.setContenido(mensaje_enviar);
EnviarCadena = new DataOutputStream(soquet.getOutputStream());
EnviarCadena.writeUTF(json.toJson(modeloOutput));
DataInputStream RecibirConfirmacion= new DataInputStream(soquet.getInputStream());
RecibirConfirmacion.readUTF();
} catch (IOException ex) {
Logger.getLogger(Procesos.class.getName()).log(Level.SEVERE, null, ex);
}
}
public static void RecibirPeticiones()
{
Gson jayson= new Gson();
Comunicacion modelo = new Comunicacion();
try {
DataInputStream dataInput= new DataInputStream(soquet.getInputStream());
modelo= jayson.fromJson(dataInput.readUTF(), Comunicacion.class);
mensajeria(modelo);
} catch (IOException e) {
e.getMessage();
}
}
public static void mensajeria(Comunicacion modelo)
{
Gson json= new Gson();
String data = json.toJson(modelo.getContenido());
switch(modelo.getTipo())
{
case SEND_MENSAJE:
MensajeRecibido(json.fromJson(data, Mensaje.class));
break;
case SEND_GRUPO:
MensajeGrupoRecibido(json.fromJson(data, MensajeGrupo.class));
break;
case SEND_CONECTADOS:
break;
case SEND_DESCONECTADOS:
break;
}
}
public static void MensajeRecibido(Mensaje mensaje)
{
MostrarMensajeAmigo(mensaje);
}
public static void MensajeGrupoRecibido(MensajeGrupo mensaje_grupo)
{
}
public static void Lista_Conectados(Amigo AmigosConectados)
{
}
private static void MostrarMensajeAmigo(Mensaje mensaje) {
Interfaz.createBubble(mensajes, Boolean.TRUE, Boolean.FALSE, mensaje.getContenido(), null);
}
}
And this is my class HomeController:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package guichat;
import com.google.gson.Gson;
import guichat.Components.CButton;
import guichat.Modelos.Comunicacion;
import guichat.Modelos.Mensaje;
import guichat.Modelos.MensajeGrupo;
import guichat.Modelos.Amigo;
import guichat.Modelos.Grupo;
import guichat.Modelos.Usuario;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.Socket;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import guichat.Procesos;
import java.net.ServerSocket;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* FXML Controller class
*
* @author Wero
*/
public class HomeController implements Initializable,Runnable {
String ip;
// Controles implementados en Interfaz
@FXML private Button closeWindowBtn, minimizeWindowBtn, outBtn, groupBtn, deleteBtn, editBtn;
@FXML private TextArea txtMessage;
@FXML private VBox messagesVBox, groupsVBox, friendsVBox;
@FXML private TextField txtCurrentContact;
// Variables de control internas
private Boolean type = false;
private String username;
private Boolean flagEdit = false;
private Boolean typeEdit = true;
private String contact;
private String[] users = {
"Arturo Carrillo",
"Kevin Alan",
"Vanya Martínez",
"Jimena Zaragoza",
"Juan Antonio",
"Emiliano Moreno",
"Eduardo Fuentes"
};
/**
* Método para hacer pruebas en la pantalla
*/
public void insertContent(){
Boolean flag = false;
for(String user : users){
Interfaz.createBubble(messagesVBox, flag, type, user, null);
if(flag) flag = false;
else flag = true;
}
int contador = 0;
for(String user : users){
createFriend(user, contador);
contador++;
}
for(String user : users){
createGroup(user, contador);
contador++;
}
}
/**
* Método para crear el boton del grupo
* @param name String Nombre del grupo
* @param id int Identificador en la base de datos del grupo
*/
public void createGroup(String name, int id){
CButton group = new CButton(name);
group.setIdElement(id);
group.setNameElement(name);
group.getStyleClass().add("chat-btn");
group.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent event) {
if(event.getSource() == group){
messagesVBox.getChildren().clear();
System.out.println("Id del Grupo: " + group.getIdElement());
txtCurrentContact.setText(group.getNameElement());
contact = group.getNameElement();
type = true;
typeEdit = true;
editBtn.setDisable(false);
deleteBtn.setDisable(false);
}
}
});
groupsVBox.getChildren().add(group);
}
/**
* Método para crear el boton del amigo
* @param name String Nombre o apodo del amigo
* @param id int Identificador en la base de datos del amigo
*/
public void createFriend(String name, int id){
CButton user = new CButton(name);
user.setIdElement(id);
user.setNameElement(name);
user.getStyleClass().add("chat-btn");
user.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent event) {
if(event.getSource() == user){
messagesVBox.getChildren().clear();
System.out.println("Id del usuario: " + user.getIdElement());
txtCurrentContact.setText(user.getNameElement());
type = false;
contact = user.getNameElement();
editBtn.setDisable(false);
deleteBtn.setDisable(false);
typeEdit = false;
}
}
});
friendsVBox.getChildren().add(user);
}
/**
* Editar información de contacto
* @param e
*/
@FXML
public void editContact(ActionEvent e){
if(!flagEdit){
editBtn.setText("Aceptar");
txtCurrentContact.setDisable(false);
flagEdit = true;
txtCurrentContact.requestFocus();
}else{
if(!txtCurrentContact.getText().equals(contact)){
System.out.println("Nuevo nombre del elemento: " + txtCurrentContact.getText());
contact = txtCurrentContact.getText();
}
editBtn.setText("Editar");
txtCurrentContact.setDisable(true);
flagEdit = false;
}
}
@FXML
public void deleteContact(ActionEvent e){
System.out.println("Falta implementar funcionalidad del botón de eliminar");
}
/**
* Método para poder cerrar la pestaña
* @param e ?
*/
@FXML
public void handleCloseWindow(ActionEvent e){
Stage stage = (Stage) closeWindowBtn.getScene().getWindow();
stage.close();
}
/**
* Método para minimizar la pestaña
* @param e
*/
@FXML
public void handleMinimizeWindow(ActionEvent e){
Stage stage = (Stage) minimizeWindowBtn.getScene().getWindow();
stage.setIconified(true);
}
/**
* Método para pasar el nombre de usuario de una vista a otra
* @param username String Nombre de Usuario
*/
public void setUsername(String username){
this.username = username;
System.out.println(this.username);
}
public void setip(String ip){
this.ip = ip;
System.out.println(this.ip);
}
/**
* Método para cerrar sesión del Usuario
* @param e
*/
@FXML
public void signOut(ActionEvent e){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Login.fxml"));
Stage stage = (Stage) outBtn.getScene().getWindow();
Scene scene = new Scene(loader.load());
stage.setScene(scene);
}catch (IOException io){
io.printStackTrace();
}
}
/**
* Método pare redirigir a la pestaña de Creación de Grupos
* @param e
*/
@FXML
public void goToCreateGroup(ActionEvent e){
try {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Groups.fxml"));
Stage stage = (Stage) groupBtn.getScene().getWindow();
Scene scene = new Scene(loader.load());
stage.setScene(scene);
GroupsController group = loader.getController();
group.setUsername(this.username);
}catch (IOException io){
io.printStackTrace();
}
}
public void sendMessage(ActionEvent e){
Interfaz.createBubble(messagesVBox, Boolean.FALSE, type, txtMessage.getText(), "werofuentes");
Procesos.EnviarMensajes(txtMessage.getText());
txtMessage.setText("");
}
/**
* Método para inicializar los componentes de la Interfaz
* @param url ?
* @param rb ?
*/
@Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
insertContent();
Procesos.mensajes = messagesVBox;
Thread hilo = new Thread(this);
hilo.start();
}
@Override
public void run()
{
System.out.println("Corriendo");
try {
ServerSocket response = new ServerSocket(7654);
System.out.println("Entre al try");
while(true) {
System.out.println("Entre al while");
Gson json = new Gson();
Socket peticion = response.accept();
DataInputStream datos = new DataInputStream(peticion.getInputStream());
String da = datos.readUTF();
Comunicacion modelo = json.fromJson(da, Comunicacion.class);
Procesos.mensajeria(modelo);
peticion.close();
response.close();
}
} catch (IOException ex) {
Logger.getLogger(HomeController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
We know that we should use Task or Platform.runLater but we don't undestand how they work.