I'm attempting to create something like a read-only console window using apache wicket.
Essentially the users submits a form to kick off a server-side operation. Then can then track the job output on the page.
I'm currently displaying the output as shown below:
public class ConsoleExample extends WebPage {
protected boolean refreshing;
private String output = "";
public void setOutput(String newOutput) {
synchronized (this) {
output = newOutput;
}
}
public void appendOutput(String added) {
synchronized (this) {
this.output = output+added;
}
}
public ConsoleExample() {
Form<ConsoleExample> form = new Form<ConsoleExample>("mainform");
add(form);
final TextArea<String> outputArea = new TextArea<String>("output",
new PropertyModel<String>(this, "output"));
outputArea.setOutputMarkupId(true);
// A timer event to add the outputArea to the target, triggering the refresh
outputArea.add(new AbstractAjaxTimerBehavior(Duration.ONE_SECOND){
private static final long serialVersionUID = 1L;
#Override
protected void onTimer(AjaxRequestTarget target) {
synchronized (this) {
if(refreshing ){
target.focusComponent(null);
target.addComponent(getComponent());
}
}
}
});
add(outputArea);
form.add(new AjaxSubmitLink("run") {
private static final long serialVersionUID = 1L;
#Override
public void onSubmit(final AjaxRequestTarget target, Form<?> form) {
setOutput("");
new Thread(new Runnable() {
#Override
public void run() {
try {
refreshing = true;
ProcessBuilder pb = new ProcessBuilder(Collections.singletonList("execute"));
pb.redirectErrorStream(true);
String line;
BufferedReader br = new BufferedReader(new InputStreamReader(pb.start().getInputStream()));
while ((line = br.readLine()) != null) {
appendOutput("\n" + line);
}
} catch (IOException e) {
//...
} finally {
//...
refreshing = false;
}
}
}).start();
}
});
}
The problem with this solution is that each time the AjaxTimerBehaviorRuns the refresh resets the text area properties, namely, cursor position and scroll position.
Therefore as the output increases the user can't track the output as the textarea jumps back to start every second.
Is there a better way to achieve this?
A posible easy to implement approach is to add a hidden TextField that you update throw AJAX with a AjaxTimerBehavior and then calls a JavaScript function (using AjaxRequestTarget.appendJavaScript()) that synchronizes the value of the hidden TextField with your <textarea>.
Partial Solution
Following on from jordeu's suggestion of using the appendJavaScript() function I am just adding a bit of javascript to append the new text:
protected void onTimer(AjaxRequestTarget target) {
synchronized (this) {
if(refreshing ){
if(update != null){
target.appendJavascript("var obj=document.getElementById(\"output\");var txt=document.createTextNode(\""+update+"\");obj.appendChild(txt)");
update = null;
}
}
}
}
Where the update field is any new text since the last update.
This solves the scroll issue but any user selection is still reset.
Also it doesn't seem like a "nice" solution to me.
Any further suggestions of how to improve are welcome.
Related
I'm writing an app in JavaFX that needs to occasionally load large CSV files around 1,000,000 lines long (or possibly more).
When a user clicks a button to start loading the file, a Service is started to load the contents, with a progress/cancel dialog showing in the meantime. The call() method in the Service is basically a while loop that loads another line from the CSV file on each iteration.
The problem is that when I start the service, the progress bar (indeterminate style) becomes jerky. Dragging the dialog is also jerky and laggy.
I wasn't having good luck searching on the web for a solution, but the closest I found was to put a Thread.sleep() in the loop, giving other things like GC a chance to catch up.
This solution seemed to reduce/remove the stuttering, but it would add a lot of time to loading the data. I am also guessing that the exact time to sleep would vary between different processors.
Is there any way to dynamically figure out how long/often to sleep for? Or call some method that would block for just long enough to keep the GUI responsive?
The code for my service:
public class CSVLoadingService extends Service<List<ObservableList<DoubleProperty>>> {
private ObjectProperty<File> srcFile = new SimpleObjectProperty<>();
private IntegerProperty startIndex = new SimpleIntegerProperty(0);
private ObjectProperty<Character> csvDelimeter = new SimpleObjectProperty(CSVParser.DEFAULT_SEPARATOR);
private DoubleProperty invalidCSVReplacement = new SimpleDoubleProperty(0);
private ObjectProperty<Dialog> dialog = new SimpleObjectProperty<>(null);
#Override
protected Task<List<ObservableList<DoubleProperty>>> createTask() {
return new Task<List<ObservableList<DoubleProperty>>>() {
final ObjectProperty<File> _srcFile = srcFile;
final IntegerProperty _startIndex = startIndex;
final ObjectProperty<Character> _csvDelimeter = csvDelimeter;
final DoubleProperty _invalidCSVReplacement = invalidCSVReplacement;
#Override
protected ObservableList<ObservableList<DoubleProperty>> call() throws Exception {
if (_startIndex.getValue() < 0)
throw new IllegalArgumentException("Start index can't be negative.");
if (_srcFile.getValue() == null)
throw new IllegalStateException("File can't be null.");
final ObservableList<ObservableList<DoubleProperty>> result = FXCollections.observableArrayList();
// Read the data from the CSV file.
try (final CSVReader reader = new CSVReader(new BufferedReader(new FileReader(_srcFile.getValue())),
_csvDelimeter.getValue(),
CSVParser.DEFAULT_QUOTE_CHARACTER,
_startIndex.getValue()))
{
// Read first line.
String[] csvLine = reader.readNext();
// If there is actually data, then read the rest of it.
if (csvLine == null || csvLine.length == 0) {
result.clear();
} else {
// Create columns.
for (String value : csvLine) {
result.add(FXCollections.observableArrayList());
}
// Parse the CSV reads and add them to the columns.
int iteration = 0;
do {
int i = 0;
for (String value : csvLine) {
// Convert the string to a number and add it to the column.
try {
result.get(i).add(new SimpleDoubleProperty(Double.parseDouble(value)));
} catch (NumberFormatException|NullPointerException e) {
result.get(i).add(_invalidCSVReplacement);
}
}
iteration++;
} while (!isCancelled() && null != (csvLine = reader.readNext()));
}
}
return result;
}
};
}
#Override
protected void succeeded() {
super.succeeded();
if (dialog.getValue() != null) {
dialog.getValue().close();
}
}
#Override
protected void failed() {
super.failed();
if (dialog.getValue() != null) {
dialog.getValue().close();
}
}
This is a typical scenario for using thread priorities. You want your GUI thread to have a higher priority than your background thread.
Here is my code and in my class I have declared ansResultinThread varible as follows.
protected static String ansResultinThread = "";
Whne I try to access to ansResultinThread value after the callServerForResult method is completed. I am not getting the Updated value.
public void callServerForResult(String ans, String casCmd) {
// String ParsedEquation = parseQuestionForSymPy(ans);
String ParsedEquation = ans;
final String stringUrl = "http://localhost:40001/" + casCmd + "/"
+ ParsedEquation;
new Thread(new Runnable() {
public void run() {
String Result = "";
try {
Result = GetServerResult(stringUrl);
} catch (IOException e) {
System.out.println(e);
}
ansResultinThread = Result;
}
}).start();
}
How to fix this Issue.
Well, since it happens asynchronously, you need to use the Future interface (see here: http://www.vogella.com/tutorials/JavaConcurrency/article.html , 8. Futures and Callables)
Your solution do not work because callServerForResult returns immediately after the thread is started.
Also keep in mind that using a static variable with multiple threads is generally not a good idea.
The simplest solution in my opinion is to use an AsyncTask instead of a Thread.
If you want to keep a Thread, another solution is to use a callback.
private final Handler handler = new Handler();
// Definition of the callback interface
public interface Callback {
void onResultReceived(String result);
}
public void callServerForResult(String ans, String casCmd, final Callback callback) {
// String ParsedEquation = parseQuestionForSymPy(ans);
String ParsedEquation = ans;
final String stringUrl = "http://localhost:40001/" + casCmd + "/"
+ ParsedEquation;
new Thread(new Runnable() {
public void run() {
try {
final String result = GetServerResult(stringUrl);
// Use a handler to invoke the callback on the main thread
handler.post(new Runnable() {
public void run() {
callback.onResultReceived(result);
}
});
} catch (IOException e) {
System.out.println(e);
}
}
}).start();
}
// Usage
callServerForResult("...", "...", new Callback() {
public void onResultReceived(String result) {
// Do something with the result
}
});
The Handler is necessary if the callback code needs to run on the same thread as the caller (usually the main thread)
I want to make a console(JTextPane) in my Java application to show that what is my application doing. I had tried a lot of times using different method but failed...Here is some part of my code.
MainClass
private final Form form;
println("Downloading files...");
public void println(String line)
{
System.out.println(line);
form.getConsole().print(line);
}
Form(GUI)
private TabConsole console;
tabbedPane.addTab("Console", console);
public TabConsole getConsole()
{
return console;
}
TabConsole
public void print(final String line) {
if (!SwingUtilities.isEventDispatchThread()) {
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
TabConsole.this.print(line);
}
});
return;
}
Document document = this.console.getDocument();
JScrollBar scrollBar = getVerticalScrollBar();
boolean shouldScroll = false;
if (getViewport().getView() == this.console) {
shouldScroll = scrollBar.getValue() + scrollBar.getSize().getHeight() + MONOSPACED.getSize() * 4 > scrollBar.getMaximum();
}
try
{
document.insertString(document.getLength(), line, null);
} catch (BadLocationException localBadLocationException) {
}
if (shouldScroll)
scrollBar.setValue(2147483647);
}
Is that anything wrong with my codes? Thanks for helping.
rethrow the BadLocationException as a RuntimeException, although the code looks correct. What error are you getting?
If the text is not showing up, you are probably updating the wrong Document. Be sure you're updating the Document which is used by your TextArea, or whatever you're using to display the contents.
I've got an app with several screens. In addition, I have a globally-running timer that occasionally (every minute or so) attempts to refresh their transaction data from a website and store it in a JSONArray (static JSONArray jTransactions).
When you go to the Transaction screen, the first thing it does is populate a ListView with the contents of jTransactions, and it will refresh the displayed info every few seconds. However if the web-thread is currently running, I get null values for everything.
I've got enough coder savvy to know that it's a threading issue, but I'm not experienced enough with JAVA/Android development to know how to handle it. And my Google-fu may be weak but the only answers I found either didn't apply or involved heavy rewriting.
I guess my question is this - how can I alter my code so that there's no direct collision between my activity and the fetch thread?
Also I fully accept that my code is probably ugly; as I said, I'm still learning the platform.
Here's a pared-down version of the thread I'm running:
static int iRefreshTransactions = 30000;
static boolean bRefreshingTransactions = false;
static Calendar cLastRefreshTransactions = null;
final Runnable mRefreshTransactions = new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
Thread T = new tRefreshTransactions();
T.start();
}
};
private class tRefreshTransactions extends Thread {
#Override
public void run() {
bRefreshingTransactions = true;
RetrieveTransactions();
bRefreshingTransactions = false;
handler.sendEmptyMessage(0);
}
private Handler handler = new Handler() {
#Override
public void handleMessage(Message msg) {
cLastRefreshTransactions = Calendar.getInstance();
ShowToast("cLastRefreshTransactions(): " + cLastRefreshTransactions.getTime().toLocaleString());
mHandler.postDelayed(mRefreshTransactions, iRefreshTransactions);
}
};
private Handler failhandler = new Handler() {
#Override
public void handleMessage(Message msg) {
// handle the failure somehow
}
};
}
Here's a pared-down version of the RetrieveTransactions() code:
// Retrieve the user's latest transactions from the website.
public boolean RetrieveTransactions() {
String result;
FailureReason = "";
iTransactions = 0;
// Retrieve the Page.
result = GetPage(Url);
// Strip the transactions from the page and convert them to a JSONArray.
try {
String sTransactions = textExtract(result, "var dataTable1Data=", ";\n", 0);
jTransactions = new JSONArray(sTransactions);
iTransactions = jTransactions.length();
return true;
} catch (JSONException e1) {
// Generally if it fails during this, there was no JSONArray to parse (hence no transactions).
FailureReason = "No Transactions Found";
return false;
}
}
And finally here's the pared-down code that displays the transactions in a listview, which is called at activity launch and every 5 seconds or so thereafter:
public void ShowTransactions() {
try {
if (!bRefreshingTransactions) {
if (iTransactions==0) {
return;
}
if (iTransactions==0) return;
List<String> listContents = new ArrayList<String>(iTransactions);
for (int i = 0; i < iTransactions; i++) {
listContents.add(jTransactions.getString(iTransactions - i - 1));
}
lvRecentTransactions.setAdapter(new ArrayAdapterTransactions(MyContext, listContents));
}
} catch (Exception e) {
// Do error stuff here
}
}
Thank you in advance. :)
It seems to be mutual exclusion problem. Make jTransaction synchronized or put the jTransaction variable in synchonized block.
synchronized(jTransactions ){
String sTransactions = textExtract(result, "var dataTable1Data=", ";\n", 0);
jTransactions = new JSONArray(sTransactions);
iTransactions = jTransactions.length();
}
I didn't test the code but I hope synchronization will help you.
I am trying to create a continuous thread where a server recieves/sends messages from a client however when I try to check for a next element it gets stuck:
public void run()
{
try
{
try
{
ArrayList<Socket> connections = parent.getConnections();
in = new Scanner(socket.getInputStream());
while(true)
{
if(in.hasNextLine()) // Gets stuck here
{
String message = in.nextLine();
System.out.println("Client said " + message);
}
}
}
finally
{
socket.close();
}
}
catch(Exception e)
{
e.printStackTrace();
}
How do I make the loop not get stuck at the specified point
Assuming you want to be able to deal with 'lines', I'd probably start with something like this:
public class SocketReader implements Runnable {
private final InputStream stream;
private final Queue<String> destination;
private volatile boolean active = true;
private SocketReader(InputStream stream, Queue<String> destination) {
this.stream = stream;
this.destination = destination;
}
public static SocketReader getReader(Socket toRead, Queue<String> destination) throws IOException {
return new SocketReader(toRead.getInputStream(), destination);
}
public void shutdown() {
active = false;
}
public void run() {
while(active) {
if (stream.hasNextLine() && active) {
final String line = stream.nextLine;
destination.add(line);
}
}
try {
stream.close();
} catch (IOException e) {
// Log somewhere
}
}
}
Drop this into its own thread (or as part of a thread or executor pool, really), and you've made the rest of your application non-blocking with regards to this code. EXPECT this to block while waiting for updates from stream.hasNextLine(). You can even supply a BlockingQueue if you don't wish to actively poll a queue, but are handling updates in some other fashion.
You can then do something like this for output:
public class QueuedPrinter implements Runnable {
private final Queue<String> input;
private final PrintStream destination;
private volatile boolean active;
public QueuedPrinter(Queue<String> input, PrintStream destination) {
this.input = input;
this.destination = destination;
}
public void shutdown() {
active = false;
}
public void run() {
while(active) {
final String line = input.poll();
if (line != null && active) {
destination.println(line);
}
}
}
}
Please note that I haven't tested this, and you may have to adjust things slightly for other Checked exceptions. You probably need to put in additional error-checking code (null-handling comes to mind). Also, this isn't completely threadsafe, but is likely to be 'good enough' for most uses.