I'm writing a java data transmission program with javafx. Everything runs fine, but when i hit the "send"button in Scene 2 on my program (and the receive button in Scene 3) it crashes my program without any error appearing.
This is my controller class for my Scene 2.
The problem lies probably in function "send()". This is is the code for creating port and transfer file.
public class ControllerSc2 {
#FXML
public Button browsebut,sendbut,backbut;
#FXML
public TextField Path;
#FXML
public TextArea info;
public Socket socket;
public String path,name;
public void browseButton() {
FileChooser fc = new FileChooser();
File sf = fc.showOpenDialog(null);
if(sf != null){
Path.setText(path = sf.getAbsolutePath());
info.setText("Name: "+ (name = sf.getName()) +"\n" + "Size: "+ sf.length() +"Bytes \n" +"From Path: "+ sf.getAbsolutePath() +"\n");
}
else{
Path.setPromptText("please choose something!");
info.setPromptText("No File was chosen!");
}
}
public void back(){
Main.ShowScene1();
}
//NOTE!!!!!!: critical problem in send function
public void send() throws IOException{
ServerSocket serverSocket = new ServerSocket(5000);//this is the code making the program listen to connection from a port
info.setText("Waiting for connection");
socket = serverSocket.accept();//waiting connection
info.setText("Connected....");
/*DataOutputStream dout=new DataOutputStream(socket.getOutputStream());//
dout.writeUTF(name);//transfer name */
//File transferFile = new File(path);
File transferFile = new File("C:\\Users\\longt\\IdeaProjects\\a.docx");
byte[] bytearray = new byte[(int) transferFile.length()];
FileInputStream fin = new FileInputStream(transferFile);
BufferedInputStream bin = new BufferedInputStream(fin);
bin.read(bytearray, 0, bytearray.length);
OutputStream os = socket.getOutputStream();
System.out.println("Sending Files...");
os.write(bytearray, 0, bytearray.length);
os.flush();
socket.close();
System.out.println("File transfer complete");
}
}
`
Related
maybe someone will explain. I have a method, that sets label text if login is successful.
#FXML
private void loginUser(ActionEvent event) throws IOException {
String user = username.getText();
String pass = password.getText();
if(validateFields(user, pass) && validateLogin(user, pass)) {
welcome.setText("Welcome, " + globalUser.getUserName()); //works
infoLine.setText("Redirecting to main dashboard..."); //works
}
}
And if I add additional code, which changes the scene after login, the label text is not changing:
#FXML
private void loginUser(ActionEvent event) throws IOException {
String user = username.getText();
String pass = password.getText();
if(validateFields(user, pass) && validateLogin(user, pass)) {
welcome.setText("Welcome, " + CurrentUser.getCurrentUser().getUserName());//not working
infoLine.setText("Redirecting to main dashboard..."); //not working
//Changing scene after successful login
Parent home = FXMLLoader.load(getClass().getResource(ScenePath.HOME.getPath()));
Scene homeScene = new Scene(home);
Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
appStage.setScene(homeScene);
appStage.show();
}
}
How to solve this problem?
My controller class looks like this. Nothing special. After 2 validations it set texts of labels and changes scenes.
public class LoginController {
#FXML
private TextField username;
#FXML
private PasswordField password;
#FXML
private Label infoLine;
#FXML
private Label welcome;
#FXML
private Button exitBtn;
UserDao userDao = new UserDao();
#FXML
private void initialize() {
close();
}
#FXML
private void loginUser(ActionEvent event) throws IOException {
String user = username.getText();
String pass = password.getText();
if(validateFields(user, pass) && validateLogin(user, pass)) {
welcome.setText("Welcome, " + CurrentUser.getCurrentUser().getUserName());
infoLine.setText("Redirecting to main dashboard...");
//Changing scene after successful login
Parent home = FXMLLoader.load(getClass().getResource(ScenePath.HOME.getPath()));
Scene homeScene = new Scene(home);
Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
appStage.setScene(homeScene);
appStage.show();
}
}
private boolean validateFields(String userName, String password) {
if (userName.isEmpty() || password.isEmpty()) {
infoLine.setText("Username and password can't be empty!");
return false;
}
return true;
}
private synchronized boolean validateLogin(String userName, String password) {
User user = userDao.getConnectedUser(userName, password);
if (user == null) {
infoLine.setText("User not found!");
return false;
}
CurrentUser.setCurrentUser(user);
return true;
}
private void close() {
exitBtn.setOnAction(SceneController::close);
}
}
Basically, the text is changing. The problem is as soon as the text is changed, you load the next view. This is happening really fast. The solution is to slow things down. Mainly, give the user time to see the text change before loading the new view. This can be done using PauseTransition.
After the text change, try the following code. After the text changes, this code gives a two-second delay before loading the new view.
PauseTransition pause = new PauseTransition(Duration.seconds(2));
pause.setOnFinished(
e -> {
Parent home = FXMLLoader.load(getClass().getResource(ScenePath.HOME.getPath()));
Scene homeScene = new Scene(home);
Stage appStage = (Stage) ((Node) event.getSource()).getScene().getWindow();
appStage.setScene(homeScene);
appStage.show();
}
);
pause.play();
I have a problem how to correctly update Java FX UI. I'm moving from Swing to FX for the first time and also ExecutorService. Problem is I need to show gif file and update progress bar during code execution but instead I get white screen with mouse loading icon. The gif and progressbar are eventually showed but after finishing this for cycle: for (int i = 0; i < futures.size(); i++)
I thought that running tasks in ExecutorService is in separate thread and using Platform.runLater for progressbar will separate UI from long running code in ExecutorService. Can you give me some explanation what is going on please?
Controller.java:
public void initialize() {
ivGif.setImage(new Image(Main.class.getResourceAsStream("/test/loading.gif")));
}
public void synchronizeFiles() {
Platform.runLater(() -> pbDownloading.setVisible(true));
ExecutorService pool = Executors.newFixedThreadPool(1);
ArrayList<Future<Boolean>> futures = new ArrayList<>();
File localFile = new File(simplified code here);
Future<Boolean> f = pool.submit(new DownloadTask(new URL(simplified code here), localFile));
futures.add(f);
for (int i = 0; i < futures.size(); i++) {
final int position = i;
Platform.runLater(() -> {
pbDownloading.setProgress(position / (double) futures.size());
});
if (!futures.get(i).get(600, TimeUnit.SECONDS)) {
System.out.println("ShutdownNow");
pool.shutdownNow();
}
}
}
DownloadTask.java:
public class DownloadTask implements Callable<Boolean> {
protected Category cat = Category.getInstance(DownloadTask.class.getName());
private URL fileURL;
private File toPath;
public DownloadTask(URL fileURL, File toPath) {
this.fileURL = fileURL;
this.toPath = toPath;
}
private void downloadFile(URL fileURL, File toPath) throws IOException {
ReadableByteChannel readableByteChannel = Channels.newChannel(fileURL.openStream());
if (!toPath.getParentFile().exists()) {
if (!toPath.getParentFile().mkdirs()) throw new IOException("Unable to create parent dirs for file: "+toPath.getAbsolutePath());
}
FileOutputStream fileOutputStream = new FileOutputStream(toPath);
fileOutputStream.getChannel().transferFrom(readableByteChannel, 0, Long.MAX_VALUE);
URLConnection urlConnection = fileURL.openConnection();
if (!toPath.setLastModified(urlConnection.getLastModified())) cat.error("Unable to write modified time stamp for file: "+toPath.getAbsolutePath());
}
#Override
public Boolean call() {
try {
downloadFile(fileURL, toPath);
} catch (IOException e){
System.out.println(e);
cat.error(e, e);
return false;
}
return true;
}
}
Thank you
EDIT: I didn't realize that the main class is running from FX Application Thread. So I had reverse threads and just applied the new thread (() -> {...}).Start(); in the main class in the start method and it's ok.
apologies for the length of my code. I realized last night that I was on the wrong path and now have gotten stuck on an issue that I think relates to JavaFX event handling. Initially I had the logic functioning outside a GUI in a basic loop that depended on interaction through the console. Everything was working great. I've now tried to get this to work in a GUI with interaction from the user.
I have two main problems with the code below.
The first is that the text in textArea is not updating with additional text after the startButton executes the start of my main logic sequence. The first append starts right under the first while loop. I was hoping to have this show up in the GUI as the logic executes. I'm not sure if I need to tell the GUI to update at certain intervals or if there's something else wrong.
Second, I'm not sure how to get the program to wait for the user to type in something into textField before hitting the textButton I created to continue on. I used to have a scanner created which caused the program to wait in the console for input. I realize I need some way of telling it to wait for a button press when it's running inside JavaFX.
I chose not to include the rest of the code to make things easier to read, but I can add it on if it will help resolve this issue.
Thank you everyone for your help!
public class FxApp extends Application {
//Creates FileParser object with methods that alter the incoming Array of Strings into the format we need
FileParser fileParser = new FileParser();
Configure configure = new Configure();
private String text;
private String initialState;
private ArrayList<Machine> machines = new ArrayList<Machine>();
private Map<String, String> initialStates = new HashMap<String, String>();
private Map<String, String> states = new HashMap<String, String>();
private Map<String, ArrayDeque<String>> queues = new HashMap<String, ArrayDeque<String>>();
private Map<Integer, ArrayList<String>> parsedData = new HashMap<Integer, ArrayList<String>>();
#Override
public void start(Stage primaryStage) throws Exception {
primaryStage.setTitle("File Chooser");
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters().addAll(new ExtensionFilter("Text Files", "*.txt"));
Button startButton = new Button("Start");
Button openButton = new Button("Click to open a file...");
openButton.setPrefSize(200, 80);
Button textButton = new Button("Enter");
TextArea textArea = new TextArea();
textArea.setWrapText(true);
TextField textField = new TextField();
Label lbl = new Label();
VBox vbox = new VBox(lbl, openButton, startButton, textArea, textField, textButton);
vbox.setSpacing(10);
vbox.setPadding(new Insets(15));
lbl.setText("This tool creates virtual automata based \ron the file.");
Scene scene = new Scene(vbox, 640, 480);
primaryStage.setScene(scene);
primaryStage.show();
openButton.setOnAction(
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
File file = fileChooser.showOpenDialog(primaryStage);
if (file != null) {
//Execute the method to convert to string array before sending to file parser
try {
fileParser.convertFile(file);
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
});
textButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
text = textField.getText();
}
});
startButton.setOnAction(new EventHandler <ActionEvent>()
{
public void handle(ActionEvent event)
{
machineCreation();
String exit = "no";
String nextLine = null;
ArrayList<String> listOfCurrentTransitions = new ArrayList<String>();
int nextInt = 0;
states = initialStates;
while(!(exit.toLowerCase().equals("yes"))) {
textArea.appendText("Choose a state to load");
//Print out the states possible for each machine
ArrayList<String> tempTrans = machines.get(nextInt).getTransitions();
//This loops through the list of transitions of the machine and pulls possible transitions from its current state
for(int i = 0; i < tempTrans.size(); i++) {
String pull = tempTrans.get(i);
String[] apart = pull.split(" ");
pull = apart[0];
if(states.get(Integer.toString(nextInt)).equals(pull)) {
listOfCurrentTransitions.add(tempTrans.get(i));
}
}
if(!(listOfCurrentTransitions.isEmpty())) {
textArea.appendText("The following transitions are possible. Choose one: " + listOfCurrentTransitions);
}
else {
textArea.appendText("No transitions for this machine exist from its current state");
}
//Tell GUI to wait for user input in textField and execute textButton which assigns to String text. Resume on button click.
The while loop blocks the JavaFX application thread which prevents updates of the GUI and handling of events.
You need to execute the logic of a single iteration of the loop on each "text commit" instead:
private TextArea textArea;
private void activateState(int nextInt) {
ArrayList<String> listOfCurrentTransitions = new ArrayList<String>();
textArea.appendText("Choose a state to load");
//Print out the states possible for each machine
ArrayList<String> tempTrans = machines.get(nextInt).getTransitions();
//This loops through the list of transitions of the machine and pulls possible transitions from its current state
for(int i = 0; i < tempTrans.size(); i++) {
String pull = tempTrans.get(i);
String[] apart = pull.split(" ");
pull = apart[0];
if(states.get(Integer.toString(nextInt)).equals(pull)) {
listOfCurrentTransitions.add(tempTrans.get(i));
}
}
if(listOfCurrentTransitions.isEmpty()) {
textArea.appendText("No transitions for this machine exist from its current state");
} else {
textArea.appendText("The following transitions are possible. Choose one: " + listOfCurrentTransitions);
}
}
#Override
public void start(Stage primaryStage) throws Exception {
...
textArea = new TextArea();
...
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
machineCreation();
activateState(0);
}
});
textButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
// read input and ask for more input...
int nextState = Integer.parseInt(textField.getText()); // TODO: deal with invalid input
activateState(nextState);
}
});
You probably need to fix the logic a bit to verify a transition is valid, change the values of some fields ect...
Using this code and the hardcoded url, the graph in the url doesn't load. I tried disabling my firewall and running it under JDK 9 with a pre-release build of Netbeans 9. No luck. A WebSocket test says that WebSocket is working fine (url commented out in the code). Any ideas what's wrong or what I should check? My guess is it has to do with WebSocket regardless of the test results. I'm using Netbeans 8.2 and jdk1.8.0_144.
//
// Code from https://stackoverflow.com/questions/42297864/javafx-webview-in-java-project
//
public class FXWebViewInSwing {
private JFXPanel jfxPanel;
public void createAndShowWindow() {
JFrame frame = new JFrame();
JButton quit = new JButton("Quit");
quit.addActionListener(event -> System.exit(0));
jfxPanel = new JFXPanel();
Platform.runLater(this::createJFXContent);
JPanel buttonPanel = new JPanel();
buttonPanel.add(quit);
frame.add(BorderLayout.CENTER, jfxPanel);
frame.add(BorderLayout.SOUTH, buttonPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1024, 576);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private void createJFXContent() {
WebView webView = new WebView();
webView.getEngine().load("https://www.tradingview.com/chart/bKsZf5LY/");
// webView.getEngine().load("https://websocket.org/echo.html");
Scene scene = new Scene(webView);
jfxPanel.setScene(scene);
}
public static void main(String[] args) {
FXWebViewInSwing swingApp = new FXWebViewInSwing();
SwingUtilities.invokeLater(swingApp::createAndShowWindow);
}
}
My application having the two Screens.
Screen-1:
It's having two buttons and one Label
1) Download:
If we click on this, then we will start the downloading process but we still in screen1 and allow you to access the "Navigate" control.
2) Navigate: If we click on this, Then we will redirect to the screen-2.
Screen-2:
1)Back: If we click on this, then we will back to the Screen-1.
While downloading process, I want to allow the user to access the other controls as well. If we started the download process and navigates to some other screen and redirects to the download screen, then we will show the current downloading progress instead of opening it as fresh. For this, I implemented like following. I created one class for implementing this download process but I am unable to update the UI of the screen from that class. Please help me on this.
Screen-1
public class MainsceneController implements Initializable {
#FXML
Button Download, Navigate;
#FXML
Label percentage;
#FXML
HBox progTag;
SyncService service = new SyncService(MainsceneController.this);
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML
void DownlaodManager() {
service.downloadProjectFiles();
}
#FXML
void Naviagtion() {
URL location = SecondSceneController.class.getResource("SecondScene.fxml");
ViewManager.getInstance().setView(location);
}
}
Screen-2:
public class SecondSceneController implements Initializable {
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
#FXML
void goBack() {
URL location = MainsceneController.class.getResource("mainscene.fxml");
ViewManager.getInstance().setView(location);
}
}
Background downloading task
public class SyncService {
long currentDownload, totalFileSize;
MainsceneController controller;
DownloadingFilesTask downloadingFilesTask;
public SyncService(MainsceneController controller) {
this.controller = controller;
downloadingFilesTask = new DownloadingFilesTask();
}
public void downloadProjectFiles() throws IOException {
DownloadingFilesTask downloadingFilesTask = new DownloadingFilesTask();
downloadingFilesTask.progressProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> ov, Number oldProgress, Number newProgress) {
System.out.println("Progress changed");
controller.percentage.setText("Progress changed:" + currentDownload);
}
});
downloadingFilesTask.stateProperty().addListener(new ChangeListener<Worker.State>() {
#Override
public void changed(ObservableValue<? extends Worker.State> source, Worker.State oldState, Worker.State newState) {
if (newState.equals(Worker.State.SUCCEEDED)) {
System.err.println("Completed downloading files");
controller.percentage.setText("Progress changed:" + currentDownload);
}
}
});
//progress listeners.
ProgressBar bar = new ProgressBar();
bar.progressProperty().bind(downloadingFilesTask.progressProperty());
bar.visibleProperty().bind(downloadingFilesTask.runningProperty());
controller.progTag.getChildren().clear();
controller.progTag.getChildren().add(bar);
new Thread(downloadingFilesTask).start();
}
class DownloadingFilesTask extends Task<Void> {
#Override
protected Void call() throws Exception {
try {
String fullUrl = "http://s3-us-west-2.amazonaws.com/absprod/media/25/manuals/53454a73d9eda$$53454a73d9f571397049971.mp4";
String destLocation = "C:\\Users\\naresh.repalle\\Desktop\\ABS Test\\53454a73d9eda$$53454a73d9f571397049971.mp4";
File destFile = new File(destLocation);
URL downloadingUrl = new URL(fullUrl);
RandomAccessFile file = null;
InputStream stream = null;
int downloaded = 0;
int size = -1;
try {
// Open connection to URL.
HttpURLConnection connection = (HttpURLConnection) downloadingUrl.openConnection();
// Specify what portion of file to download.
connection.setRequestProperty("Range", "bytes=" + downloaded + "-");
connection.setConnectTimeout(10 * 1000);
connection.setReadTimeout(10 * 1000);
// Connect to server.
connection.connect();
// Make sure response code is in the 200 range.
if (connection.getResponseCode() / 100 != 2) {
System.err.println("Wrong response code while downloading file." + connection.getResponseCode());
}
// Check for valid content length.
int contentLength = connection.getContentLength();
if (contentLength < 1) {
System.err.println("Wrong file size while downloading file." + contentLength);
}
/*
* Set the size for this download if it hasn't been already set.
*/
if (size == -1) {
size = contentLength;
}
totalFileSize = size;
// Open file and seek to the end of it.
file = new RandomAccessFile(destFile, "rw");
file.seek(downloaded);
stream = connection.getInputStream();
int MAX_BUFFER_SIZE = 1024;
while (true) {
/*
* Size buffer according to how much of the file is left to download.
*/
byte buffer[];
if (size - downloaded > MAX_BUFFER_SIZE) {
buffer = new byte[MAX_BUFFER_SIZE];
} else {
buffer = new byte[size - downloaded];
}
// Read from server into buffer.
int read = stream.read(buffer);
if (read == -1) {
System.out.println("read: " + read);
break;
}
// Write buffer to file.
file.write(buffer, 0, read);
downloaded += read;
currentDownload = downloaded;
stateChanged();
}
} catch (Exception e) {
System.err.println("Exception in Downloading file: " + e.toString());
} finally {
/*
* Change status to complete if this point was reached because downloading
* has finished.
*/
// Close file.
if (file != null) {
try {
file.close();
} catch (Exception e) {
}
}
// Close connection to server.
if (stream != null) {
try {
stream.close();
} catch (Exception e) {
System.err.println("exception in downloading: " + e.toString());
}
}
}
} catch (Exception ex) {
System.err.println("Unable to download file: " + ex);
}
return null;
}
private void stateChanged() {
updateProgress(currentDownload, totalFileSize);
}
}
}
Your SyncService doesn't need to reload the FXML; it just needs to communicate with the existing controller. A simple way to do this would be to just pass a reference to SyncService's constructor:
public class SyncService {
MainSceneController controller ;
public SyncService(MainSceneController controller) {
this.controller = controller ;
}
// ...
public void downloadProjectFiles() throws IOException {
DownloadingFilesTask downloadingFilesTask = new DownloadingFilesTask();
// Remove the following:
// URL location = MainsceneController.class.getResource("mainscene.fxml");
// FXMLLoader fxmlLoader = new FXMLLoader();
// fxmlLoader.setLocation(location);
// fxmlLoader.setBuilderFactory(new JavaFXBuilderFactory());
// final AnchorPane root = (AnchorPane) fxmlLoader.load(location.openStream());
//get the controller
// final MainsceneController controller = (MainsceneController) fxmlLoader.getController();
// Then code as before
// ...
}
}
I think I would actually do it differently: I don't like the strong coupling between the SyncService and the MainSceneController. I would initialize the progress bar in the MainSceneController, expose the progress as a property in the SyncService and bind to it in the controller. But you should be able to use the simpler approach to get it working.