Skip to content
Snippets Groups Projects
Commit a7b96146 authored by Radek Ošlejšek's avatar Radek Ošlejšek
Browse files

Resolve "BUG: The application freezes in 1:1 analysis"

parent a216b006
No related branches found
No related tags found
No related merge requests found
package cz.fidentis.analyst.core;
import cz.fidentis.analyst.Logger;
import java.awt.BorderLayout;
import java.util.List;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
/**
* Wrapper of the default Java Netbeans Platform output window.
* This wrapper runs in the worker thread, reads messages logged through
* the {@link cz.fidentis.analyst.Logger} and displays them in the output window.
*
* The {@code OutputWindow} is currently executed from
* the {@link cz.fidentis.analyst.core.ProjectTopComp}, which is always presented.
*
* @author Radek Oslejsek
*/
public class OutputWindow extends SwingWorker<String, String> {
private static JTextArea textArea;
/**
* Constructor.
*/
public OutputWindow() {
TopComponent outputWin = WindowManager.getDefault().findTopComponent("output");
textArea = new JTextArea();
JScrollPane sp = new JScrollPane(textArea);
outputWin.add(sp, BorderLayout.CENTER);
}
/**
* Reads messages of the {@code Logger} in the background and re-sends them
* the {@link #process(java.util.List)} method.
*
* @return Complete text displayed in the output window.
* @throws Exception
*/
@Override
protected String doInBackground() throws Exception {
String line = Logger.reader().readLine();
while (line != null) {
publish(line);
line = Logger.reader().readLine();
}
return textArea.getText();
}
/**
* Takes text lines process by the {@link #doInBackground()} method and
* adds them to the output window.
*
* @param chunks process lines
*/
@Override
protected void process(List<String> chunks) {
StringBuilder sb = new StringBuilder();
sb.append(textArea.getText());
chunks.forEach(str -> {
sb.append(str + System.lineSeparator());
});
textArea.setText(sb.toString());
}
}
package cz.fidentis.analyst.core;
import cz.fidentis.analyst.Logger;
import java.awt.BorderLayout;
import java.io.IOException;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import org.openide.util.Exceptions;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
/**
* Wrapper of the default Java Netbeans Platform output window.
* This singleton runs in the background, reads messages logged through
* the {@link cz.fidentis.analyst.Logger} and displays them in the output window.
*
* This singleton is currently executed from
* the {@link cz.fidentis.analyst.core.ProjectTopComp}, which is always presented
* in the FIDENTIS application.
*
* @author Radek Oslejsek
*/
public class OutputWindowThread extends Thread {
private JTextArea textArea;
private static OutputWindowThread instance;
protected OutputWindowThread() {
TopComponent outputWin = WindowManager.getDefault().findTopComponent("output");
textArea = new JTextArea();
JScrollPane sp = new JScrollPane(textArea);
outputWin.add(sp, BorderLayout.CENTER);
}
/**
* Starts the redirection of messages logged via the {@link cz.fidentis.analyst.Logger}
* into the output window.
*
* @return {@code false} if the singleton/thread is already executed,
* {@code true} otherwise.
*/
public static boolean execute() {
if (instance != null) {
return false;
}
instance = new OutputWindowThread();
instance.start(); // executes the run() method
Logger.redirectToPipe();
return true;
}
/**
* Stops the redirection of messages logged via the {@link cz.fidentis.analyst.Logger}
* into the output window.
*
* @param err If {@code true} then the logged messages are redirected to the system error output.
* Otherwise, the system standard output is used.
* @return {@code false} if the singleton/thread is already stopped,
* {@code true} otherwise.
*/
public static boolean stopExecution(boolean err) {
if (instance == null) {
return false;
}
if (err) {
Logger.redirectToStdErr();
} else {
Logger.redirectToStdOut();
}
instance = null;
return true;
}
/**
* Stops the redirection of messages logged via the {@link cz.fidentis.analyst.Logger}
* into the output window. The messages are redirected into the system standard output.
*
* @return {@code false} if the singleton/thread is already stopped,
* {@code true} otherwise.
*/
public static boolean stopExecution() {
return stopExecution(false);
}
@Override
public void run() {
int errorCounter = 0;
while (true) {
try {
String line = Logger.read();
if (line != null) {
StringBuilder sb = new StringBuilder();
sb.append(textArea.getText())
.append(line)
.append(System.lineSeparator());
textArea.setText(sb.toString());
textArea.setCaretPosition(textArea.getDocument().getLength());
errorCounter = 0;
} else {
//System.out.println("No datain the message pipe");
fallAsleep(2000);
}
} catch (IOException ex) {
if (errorCounter < 8) {
errorCounter++;
}
System.out.println("IOException: " + ex);
fallAsleep(errorCounter * 1000);
}
}
}
protected void fallAsleep(int mil) {
try {
sleep(mil);
} catch (InterruptedException ex) {
Exceptions.printStackTrace(ex);
}
}
}
......@@ -81,9 +81,9 @@ public final class ProjectTopComp extends TopComponent {
putClientProperty(TopComponent.PROP_DRAGGING_DISABLED, Boolean.TRUE);
putClientProperty(TopComponent.PROP_UNDOCKING_DISABLED, Boolean.TRUE);
// Execute OutputWindow in the worker thread so that messages
// logged via Logger are displayed in the output window
(new OutputWindow()).execute();
// Execute infinite OutputWindowThread that redirects messages logged
// via Logger into the standard output window
OutputWindowThread.execute();
}
/**
......
......@@ -306,8 +306,6 @@ public class RegistrationAction extends ControlPanelAction {
* otherwise set color to default
*/
private void calculateFeaturePoints() {
Logger.print("Calculation of feature points");
if (getPrimaryDrawableFace() == null) { // scene not yet initiated
return;
}
......
......@@ -14,16 +14,17 @@ import java.util.logging.Level;
* A logging class that can be used from anywhere to display debugging or other
* messages.
* <p>
* Messages are printed into the standard output unless the {@link #reader()}
* method is called. Then the messages are redirected to the returned {@code BufferedReader}.
* Messages are printed into the system standard output by default. But can be also
* redirected to system error output or into the pipe.
* </p>
* <p>
* The implementation supposes that the caller of the {@link #reader()} method
* (or other object} will read the messages from the {@code BufferedReader} forever.
* Otherwise, the program can freeze due to the full buffer.
* If the messages are redirected into pipe, then <b>a consumer has to be running
* concurrently</b> and reading the data through the {@link #read()} method.
* <b>Otherwise, the program can freeze due to the full buffer!</b>
* </p>
* <p>
* FIDENTIS application automatically redirects logged messages to the output window.
* FIDENTIS application automatically redirects logged messages to the output window
* via the {@code OutputWindowThread}.
* </p>
* <p>
* <b>Usage:</b>
......@@ -39,6 +40,8 @@ import java.util.logging.Level;
*/
public final class Logger {
private static final int PIPE_SIZE = 10240; // default is 1024
private static PrintStream out = System.out;
private static BufferedReader in;
......@@ -48,21 +51,53 @@ public final class Logger {
}
/**
* Redirects logs from standard output to the {@code BufferedReader}.
* Redirects messages into a pipe.
* <b>A consumer has to be running concurrently</b> and reading the messages
* through the {@link #read()} method.
* Otherwise, the program can freeze due to the full buffer!
*/
public static synchronized void redirectToPipe() {
if (in != null) {
return;
}
PipedOutputStream pOut = new PipedOutputStream();
out = new PrintStream(pOut);
try {
in = new BufferedReader(new InputStreamReader(new PipedInputStream(pOut, PIPE_SIZE)));
} catch (IOException ex) {
java.util.logging.Logger.getLogger(Logger.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Redirects messages into the system standard output.
*/
public static synchronized void redirectToStdOut() {
out = System.out;
in = null;
}
/**
* Redirects messages into the system error output.
*/
public static synchronized void redirectToStdErr() {
out = System.err;
in = null;
}
/**
* Returns an event message. If the logger is redirected to standard or error output,
* then {@code null} is returned. Also, if the is no message, {@code null} is returned.
*
* @return {@code BufferedReader} to which the logs are redirected.
* @return Event message or {@code null}
* @throws IOException on error in reading the message buffer.
*/
public static BufferedReader reader() {
if (in == null) {
PipedOutputStream pOut = new PipedOutputStream();
out = new PrintStream(pOut);
try {
in = new BufferedReader(new InputStreamReader(new PipedInputStream(pOut)));
} catch (IOException ex) {
java.util.logging.Logger.getLogger(Logger.class.getName()).log(Level.SEVERE, null, ex);
}
public static String read() throws IOException {
if (in == null/* || !in.ready()*/) {
return null;
}
return in;
return in.readLine();
}
/**
......@@ -76,6 +111,7 @@ public final class Logger {
+ " [duration unknown]: "
+ msg.trim();
out.println(outMsg);
out.flush();
}
/**
......@@ -112,8 +148,9 @@ public final class Logger {
duration.toMillisPart())
+ "]: "
+ msg.trim();
out.println(outMsg);
out.flush();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment