I'm having some trouble with Jline and not quite understanding how to work it properly, everything seems to work from the examples but when i attempt to move it into my console application things go weird.
I've come across two issues:
When you write input into the console while something else is logging a message via System out, the written input gets broken. (View: https://i.imgur.com/ZAJDjTI.png)
I attempted to sync the commands to the main thread since the reader thread will be blocking, but this time you'll find that this causes the output text to take over the commands input space.
((Green text is the input, white is output)View: https://i.imgur.com/CdKiIYy.png)
The output i expected was for input coming from the bottom of the console to be unaffected by the output of the console, leaving a smooth input text layer at the bottom. (View: https://i.imgur.com/HfH5l8U.png?1)
Here's an example class i wrote to demonstrate the two problems I'm having:
import jline.console.ConsoleReader;
import java.io.IOException;
import java.util.LinkedList;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.locks.ReentrantLock;
public class Example {
private ConsoleReader consoleReader;
private LinkedList<Runnable> syncQueue = new LinkedList<>();
private ReentrantLock lock = new ReentrantLock();
public Example() {
try {
this.consoleReader = new ConsoleReader();
this.consoleReader.setExpandEvents(false);
} catch (IOException e) {
e.printStackTrace();
}
//If you enable this, Jline seems to be disrupted by the System out.
// startStopwatch();
setupJline();
//Ticker, its ugly i know
while (true) {
lock.lock();
try {
while (syncQueue.size() > 0) {
Runnable runnable = syncQueue.poll();
try {
runnable.run();
} catch (Exception e) {
e.printStackTrace();
}
}
} finally {
lock.unlock();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private void setupJline() {
new Thread("Console Thread") {
#Override
public void run() {
while (true) {
try {
String line = consoleReader.readLine(">");
if (line != null && line.trim().length() > 0) {
//Lets pass this on as an instruction to our program
//Sync seems okay, output wise
handleInstructionSynced(line);
//async seems to mess things up though, comment the handleInstructionSynced method and
//uncomment the Async one to see what i mean.
//handleInstructionAsync(line);
}
consoleReader.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}.start();
}
//Just a dummy example instruction handler
private void handleInstructionAsync(String input) {
System.out.println("You've input \"" + input + "\" as your instruction.");
}
private void handleInstructionSynced(String input) {
runSync(() -> System.out.println("You've input \"" + input + "\" as your instruction."));
}
private void runSync(Runnable runnable) {
lock.lock();
try {
syncQueue.add(runnable);
} finally {
lock.unlock();
}
}
private void startStopwatch() {
Timer timer = new Timer();
TimerTask timerTask = new TimerTask() {
int time = 0;
#Override
public void run() {
System.out.println(time + " seconds counted");
time++;
}
};
timer.scheduleAtFixedRate(timerTask, 0, 1000);
}
public static void main(String[] args) {
new Example();
}
}
Any solutions?
Related
From the WebEngine docs:
Loading always happens on a background thread. Methods that initiate
loading return immediately after scheduling a background job. To track
progress and/or cancel a job, use the Worker instance available from
the getLoadWorker() method.
I have an HTML string which I load on the WebView via WebEngine.loadContent(String). That string is about 5 million chars long. Upon running that in Platform.runLater() (and I have to run it in the JavaFX thread, otherwise I get an error) my UI hangs for about a minute.
If I don't run it in Platform.runLater() I get:
java.lang.IllegalStateException: Not on FX application thread; currentThread = populator
at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:236)
at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
at javafx.scene.web.WebEngine.checkThread(WebEngine.java:1216)
at javafx.scene.web.WebEngine.loadContent(WebEngine.java:931)
at javafx.scene.web.WebEngine.loadContent(WebEngine.java:919)
...
I can't query the webEngine via webEngine.getLoadWorker().getProgress() nor cancel via webEngine.getLoadWorker().cancel() because I have to, again, run that on the JavaFX thread, which is hanged...
So I have to wait until the page loads, and then any Platform.runLater(()->webEngine.getLoadWorker().getProgress()) submitted previously (during the webpage loading process) will run, giving me 1.0 each time...
The code I'm using to query the Worker:
// WebView
wvIn.getEngine().getLoadWorker().stateProperty().addListener(new ChangeListener<Worker.State>() {
class ProgressThread extends Thread {
private Worker.State loadWorkerState;
synchronized Worker.State getLoadWorkerState() {
return loadWorkerState;
}
synchronized void setLoadWorkerState(Worker.State loadWorkerState) {
this.loadWorkerState = loadWorkerState;
}
{
setDaemon(true);
setName("LoadingWebpageProgressThread");
}
public void run() {
while (true) {
try {
if (getLoadWorkerState() == Worker.State.RUNNING)
// piWv ProgressIndicator (WebView loading)
Platform.runLater(() -> piWv.setVisible(true));
while (getLoadWorkerState() == Worker.State.RUNNING) {
Platform.runLater(() -> {
piWv.setProgress(wvIn.getEngine().getLoadWorker().getProgress());
// TODO delete
System.out.println(wvIn.getEngine().getLoadWorker().getProgress());
});
Thread.sleep(100);
}
if (getLoadWorkerState() == Worker.State.SUCCEEDED) {
Platform.runLater(() -> piWv.setProgress(1d));
Thread.sleep(100);
Platform.runLater(() -> {
piWv.setVisible(false);
piWv.setProgress(0d);
});
}
synchronized (this) {
wait();
}
} catch (InterruptedException e) {
}
}
}
};
final ProgressThread progressThread = new ProgressThread();
{
progressThread.start();
}
// executed on JavaFX Thread
#Override
public void changed(ObservableValue<? extends State> observable, State oldValue, State newValue) {
if (newValue == State.SUCCEEDED) {
JSObject window = (JSObject) wvIn.getEngine().executeScript("window");
window.setMember("controller", mainController);
progressThread.setLoadWorkerState(newValue);
progressThread.interrupt();
} else if (newValue == State.RUNNING) {
progressThread.setLoadWorkerState(newValue);
progressThread.interrupt();
}
// TODO delete
System.out.println(oldValue + "->" + newValue);
}
});
Is there anyway to force the loading in a background thread?
What exactly is happening in the JavaFX thread? Is it the process of populating the WebView?
That HTML file you showed in your question loads almost instantly on my machine.
In my opinion, the problem is in your code. Well, it's quite cumbersome. Please let me point out that it doesn't look very good, since it is deadlock-prone, and uses exceptions to implement a form of guarded-blocks.
It looks like you don't fully understand the concept of events.
Unless you have cut off the code snippet in your question in some way for the sake of brevity, the ProgressThread instance is completely useless.
You already have a ChangeListener dispatching events as needed, executing callbacks right on the JavaFX Application Thread; just use it.
indicator.setVisible(false); // Start hidden
engine.getLoadWorker().progressProperty().addListener((observable, oldValue, newValue) -> {
indicator.setProgress(newValue.doubleValue());
indicator.setVisible(indicator.getProgress() != 1D);
});
Alternatively, you could just use the power of binding:
Worker<Void> worker = engine.getLoadWorker();
indicator.visibleProperty().bind(worker.stateProperty().isEqualTo(Worker.State.RUNNING));
indicator.progressProperty().bind(worker.progressProperty());
For your JavaScript code, I suggest you to listen to document events instead of worker state events: they are more meaningful in your case, and they are fired when the page has actually finished rendering, not just when the page data has finished downloading.
engine.documentProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == null) {
return;
}
JSObject window = (JSObject)engine.executeScript("window");
window.setMember("controller", mainController);
});
Please note that on my machine, the progress appears always to be zero while loading is in progress. I think that the problem is in your HTML code: if you try to load some other resource, such us the webpage you are reading now, the progress indicator will deliver a much more enjoyable experience, being updated several times before the page contents have finished loading.
So...
I ended up building my own Http Server to test load instead of loadContent (note: it's a bad server):
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;
import java.util.zip.GZIPOutputStream;
public class BadHttpServer {
private static int request = 0, connection = 0;
private static final BadHttpServer SINGLETON = new BadHttpServer();
private ServerSocket serverSocket;
private String htmlString, formattedDate;
private byte[] compressedHTML;
public String getURL() {
return "http://localhost:60000";
}
synchronized byte[] getCompressedHTML() {
return compressedHTML;
}
synchronized void setCompressedHTML(String string) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(string.length() / 2);
GZIPOutputStream gos = new GZIPOutputStream(baos);) {
gos.write(string.getBytes("utf-8"));
gos.close();
this.compressedHTML = baos.toByteArray();
formattedDate = String.format(Locale.US, "%1$ta, %1$td %1$tb %1$tY %1$tH:%1$tM:%1$tS %1$tZ",
Calendar.getInstance(TimeZone.getTimeZone("GMT")));
} catch (IOException e) {
e.printStackTrace();
}
}
private synchronized String getFormattedDate() {
return formattedDate;
}
private BadHttpServer() {
try {
serverSocket = new ServerSocket(60000);
} catch (IOException e) {
e.printStackTrace();
}
new Thread() {
{
setDaemon(true);
setName("serverSocketThread");
}
public void run() {
while (true) {
try {
new Thread(acceptAndSendHTML(serverSocket.accept())) {
{
setDaemon(true);
setName("acceptThread" + connection++);
}
}.start();
} catch (IOException e) {
e.printStackTrace();
}
}
};
}.start();
}
protected Runnable acceptAndSendHTML(final Socket socket) {
return new Runnable() {
#Override
public void run() {
try (BufferedReader br = new BufferedReader(new InputStreamReader(socket.getInputStream(), "ASCII"));
BufferedWriter asciiWriter = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream(), "ASCII"));
BufferedOutputStream bos = new BufferedOutputStream(socket.getOutputStream(), 1000)) {
while (!socket.isClosed()) {
int request = BadHttpServer.request++;
// READING
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(br.readLine() + "\r\n");
for (String line = ""; !(line = br.readLine()).isEmpty(); stringBuilder.append(line + "\r\n"))
;
// TODO delete
System.out.print("Connection " + request + " request: " + stringBuilder);
// WRITING
byte[] compressedHTML = getCompressedHTML();
asciiWriter.write("HTTP/1.1 100 Continue\r\n");
asciiWriter.write("\r\n");
asciiWriter.write("HTTP/1.1 200 OK\r\n");
asciiWriter.write("Date: " + getFormattedDate() + "\r\n");
asciiWriter.write("Content-Type: text/html; charset=utf-8\r\n");
asciiWriter.write("Content-Encoding: gzip\r\n");
asciiWriter.write("Content-Charset: utf-8\r\n");
asciiWriter.write("Content-Length: " + compressedHTML.length + "\r\n");
// TODO delete
System.out.println("Content-Length: " + compressedHTML.length + "\r\n");
asciiWriter.write("\r\n");
asciiWriter.flush();
for (int writtenOverall = 0, writtenThisIteration = 0; writtenOverall < compressedHTML.length; writtenOverall += writtenThisIteration) {
bos.write(compressedHTML, writtenOverall,
writtenThisIteration = Math.min(compressedHTML.length - writtenOverall, 1000));
Thread.sleep(10); // Bandwidth throttling
}
bos.flush();
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
}
public static BadHttpServer getIntance() {
return SINGLETON;
}
public synchronized void setHtmlString(String htmlString) {
this.htmlString = htmlString;
setCompressedHTML(htmlString);
}
synchronized String getHtmlString() {
return htmlString;
}
}
And now, instead of loadContent, I'd
BadHttpServer.getIntance().setHtmlString(htmlString);
Platform.runLater(() -> wvIn.getEngine().load(BadHttpServer.getIntance().getURL()));
I did some benchmarks - measuring the time from once the loadWorker started RUNNING till it SUCCEDED:
via BadHttpServer:
javascript and style after body: 13,5 s
javascript and style before body: 8,6 s
so I continued to test with javascript and style before body.
without javascript injection: 8,5 s
I continued to test with javascript injection.
Next I did with loadContent: 170 s - no UI resposivness during loading
Yep - almost 3 minutes...
So I returned back with BadHttpServer.
Next I tried "throttling the bandwidth" by changing the sleep time.
no sleep: 8,2 s
5 ms sleep: 6,8 s
10 ms sleep: 6,7 s
20 ms sleep: 11,6 s
In these cases the UI was responsive, although it was lagging a bit.
The progressIndicator showed nicely, pausing a bit at 90%.
By increasing the sleep time, the UI was more responsive.
So, I'd guess the loadContent method could be a tad better, unless I'm doing something wrong implementing it...
My Timer task is not functioning as it's supposed to. I have scheduled it to repeat a specific task every 3 seconds but this is not happening.
As per Java documentations:
schedule(TimerTask task, long delay,long period) .
Schedules the specified task for repeated fixed-delay execution, beginning after the specified delay. Subsequent executions take place at approximately regular intervals separated by the specified period.
public class Tester {
public static void main(String[] args) {
log.info("Schedule task");
Timer time = new Timer();
TesterClient tc = new TesterClient();
time.schedule(tc, 0, 3000);
}
}
public class TesterClient extends TimerTask {
public void init() {
System.out.println("New Task!!!!");
}
#Override
public void run() {
init();
}
}
And yet i only get one "New Task!!!!" printed in console
Am i missing something here?
Thanks
Update:
I will try to paste in here every piece of code that is relevant and goes from top to bottom in terms of execution.
Start:
public class Tester {
public static Logger log = Logger.getLogger("com.orderlysoftware.orderlycalls.manager.ManagerClient");
public static Timer time = new Timer();
public static void main(String[] args) {
log.info("Creating service");
Service.serviceInit();
log.info("Initializing TesterClient for scheduled task");
TesterClient tc = new TesterClient();
time.schedule(tc, 0, 3000);
}
public static ManagerSettings managerSettings() {
ManagerSettings managerSettings = new ManagerSettings();
managerSettings.setName("managerClient");
managerSettings.setHost("77.237.251.152");
managerSettings.setPort(5038);
managerSettings.setUsername("orderlystats");
managerSettings.setPassword("orderlystats");
return managerSettings;
}
}
Service class method:
static ExecutorService executorService;
{
serviceInit();
}
//public static ClassLoader loader;
public static void serviceInit(){
if(executorService!=null) {
return;
}
executorService= Executors.newCachedThreadPool();
try {
ThreadPoolExecutor tpe=(ThreadPoolExecutor)executorService;
tpe.setMaximumPoolSize(100000);
} catch (Exception ex) {
System.out.println(ex);
}
}
package com.orderlysoftware.testing;
import java.io.IOException;
import java.util.TimerTask;
import java.util.concurrent.ExecutorService;
import java.util.logging.Logger;
import com.orderlysoftware.orderlycalls.OrderlyCalls;
import com.orderlysoftware.orderlycalls.asterisk.manager.ManagerClient;
import com.orderlysoftware.orderlycalls.asterisk.manager.action.ManagerResponse;
import com.orderlysoftware.orderlycalls.asterisk.manager.event.ManagerEvent;
import com.orderlysoftware.orderlycalls.asterisk.manager.event.ManagerEventListener;
import com.orderlysoftware.orderlycalls.base.Service;
public class TesterClient extends TimerTask {
public static Logger log = Logger.getLogger("com.orderlysoftware.orderlycalls.manager.ManagerClient");
public static ExecutorService es = Service.getExecutorService();
public ManagerClient mc;
public void init() {
log.info("run check method to see if Manager Client is running");
boolean running = check();
log.info("checker status is : " + running);
while(running) {
try {
Thread.sleep(3000);
startCall();
} catch (InterruptedException e) {
log.info("Sleep interrupted");
}
}
}
public boolean check() {
log.info("ManagerClient is: " + mc);
if(mc == null) {
log.info("Initialize service");
mc = (ManagerClient)OrderlyCalls.createService(ManagerClient.class, Tester.managerSettings());
log.info("Initialize ManagerClient");
mc.init();
log.info("Service created. ManagerClient initialized : "+ mc);
}
if(!mc.isConnected()) {
log.info("ManagerClient is not connected");
return false;
}
log.info("Check if ManagerClient is connected AND running");
if(mc.isConnected() && !mc.isRunning()) {
log.info("Manager Client is connected but NOT running");
return false;
}
if(mc.isConnected() && mc.isRunning()) {
log.info("ManagerClient is connected and running");
return true;
}
return false;
}
private void startCall() {
log.info("Adding listener to the call");
addListenerToCall(mc);
int testID = 0;
ManagerResponse response = null;
try {
response = mc.originate("Local/1001#main", "1001", "main", "1", null, null, 2500, "1002", "testID=" + (testID++), "1", true);
log.info("Manager response is: " + response);
if(response == null) {
mc.shutdown();
throw new IOException("Null response for originate.");
}
if(!response.getValue("Response").equals("Success")) {
mc.shutdown();
throw new IOException("Originate returned " + response.getValue("Response") + ": " + response.getValue("Message"));
}
} catch (IOException e) {
log.info("IO Exception" + e.toString());
}
}
public void addListenerToCall(ManagerClient mc) {
try {
// Add event listener
log.info("Adding ManagerEventListener to ManagerClient: " + mc);
mc.addManagerEventListener(new ManagerEventListener() {
#Override
public void handleManagerEvent(ManagerEvent event) {
if("OriginateResponse".equals(event.getType())) {
handleOriginateResponse(event);
}
}
});
} catch (IOException e) {
log.info("IO Exception : " + e);
}
}
protected void handleOriginateResponse(ManagerEvent event) {
try {
// do something here
Thread.sleep(1000);
} catch (InterruptedException e) {
log.info("sleep interupted" + e);
}
}
#Override
public void run() {
log.info("New Task!!!!!!!!!!");
init();
}
}
It works for me - but I suspect the problem is that you're letting the Timer get garbage collected:
After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur.
As noted in comments, I believe that "outstanding tasks" means "tasks that have already been started" rather than just "ones which would be scheduled". The docs are unclear, however, and I may be mistaken.
If you prevent garbage collection (e.g. by keeping a reference to the Timer in a static variable) then I think you'll see it keep going forever...
Your program works fine for me too. The issue got reproduced with following change in your program:
import java.util.*;
public class Tester {
public static void main(String[] args) {
System.out.println("Schedule task");
Timer time = new Timer();
TesterClient tc = new TesterClient();
time.schedule(tc, 0, 3000);
}
}
class TesterClient extends TimerTask {
public void init() {
System.out.println("New Task!!!!");
}
#Override
public void run() {
init();
this.cancel(); //-------This causes hang in execution after printing once
}
}
But, not sure what could have caused this to happen without cancel() in your program.
I have a piece of Java program that essentially does the following:
public static void main(String[] args)
{
while(true)
{
// does stuff ...
}
}
The infinite loop is there by design - when left alone the program will loop infinitely. For the most part it works fine. However, sometimes I want to take the program down for maintenance, and when I take it down I want to make sure that it runs through all the code in the loop to the end then exit.
I am wondering what is the best solution for this. One idea I have in mind is to do something like this:
public static void main(String[] args)
{
File f = new File("C:\exit.txt");
while(!f.exists())
{
// does stuff ...
}
}
which basically allows me to gracefully get out of the loop by creating a file called "exit.txt". This is probably OK for my purposes, but I would like to know if there are better, alternative methods.
I think that the WatchService that was introduced in Java 7 may be of use here (if you prefer a file based approach that is). From the JavaDocs:
A watch service that watches registered objects for changes and events. For example a file manager may use a watch service to monitor a directory for changes so that it can update its display of the list of files when files are created or deleted.
Basically what this means is that you can set up a WatchService that can watch a folder for changes. When a change occurs you can choose what actions to take.
The following code uses the WatchService to monitor a specified folder for changes. When a change has happened it executes a Runnable that the caller has provided (the method runWhenItIsTimeToExit).
public class ExitChecker {
private final Path dir;
private final Executor executor;
private final WatchService watcher;
// Create the checker using the provided path but with some defaults for
// executor and watch service
public ExitChecker(final Path dir) throws IOException {
this(dir, FileSystems.getDefault().newWatchService(), Executors.newFixedThreadPool(1));
}
// Create the checker using the provided path, watcher and executor
public ExitChecker(final Path dir, final WatchService watcher, final Executor executor) {
this.dir = dir;
this.watcher = watcher;
this.executor = executor;
}
// Wait for the folder to be modified, then invoke the provided runnable
public void runWhenItIsTimeToExit(final Runnable action) throws IOException {
// Listen on events in the provided folder
dir.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY);
// Run it async, otherwise the caller thread will be blocked
CompletableFuture.runAsync(() -> {
try {
watcher.take();
} catch (InterruptedException e) {
// Ok, we got interrupted
}
}, executor).thenRunAsync(action);
}
}
So, how do we use the checker then? Well, the following code illustrates this:
public static void main(String... args) throws IOException, InterruptedException {
// Setup dirs in the home folder
final Path directory = Files.createDirectories(
new File(System.getProperty("user.home") + "/.exittst").toPath());
// In this case we use an AtomicBoolean to hold the "exit-status"
AtomicBoolean shouldExit = new AtomicBoolean(false);
// Start the exit checker, provide a Runnable that will be executed
// when it is time to exit the program
new ExitChecker(directory).runWhenItIsTimeToExit(() -> {
// This is where your exit code will end up. In this case we
// simply change the value of the AtomicBoolean
shouldExit.set(true);
});
// Start processing
while (!shouldExit.get()) {
System.out.println("Do something in loop");
Thread.sleep(1000);
}
System.out.println("Exiting");
}
Finally, how do you exit the program then? Well simply touch a file in the specified folder. Example:
cd ~/.exittst
touch exit-now.please
Resources:
A good tutorial on how to use the WatchService
WatchService JavaDocs
A good article about CompletableFuture
More stuff about CompletableFuture
Why the WatchService is slow on Mac OS X
One could employ some sophisticated techniques here. The file watchdog is one option. RMI could be another. But in fact, the mechanisms that are required here are quite simple, so I'd like to propose another (very simple) solution.
Note: This solution is just one option, showing that it is possible to do it that way. It is not a general recommendation, and whether it is "good" or not depends on the application case.
The solution is simply based on Sockets. The ServerSocket#accept method already encapsulates the functionality that you want:
Listens for a connection to be made to this socket and accepts it. The method blocks until a connection is made.
Based on this, it is trivial to create such a "remote control": The server just waits for a connection, and sets a flag when the connection is opened:
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.atomic.AtomicBoolean;
class RemoteExitServer
{
private final AtomicBoolean flag = new AtomicBoolean();
RemoteExitServer()
{
Thread t = new Thread(new Runnable()
{
#Override
public void run()
{
waitForConnection();
}
});
t.setDaemon(true);
t.start();
}
private void waitForConnection()
{
ServerSocket server = null;
Socket socket = null;
try
{
server = new ServerSocket(1234);
socket = server.accept();
flag.set(true);
}
catch (IOException e)
{
e.printStackTrace();
}
finally
{
if (server != null)
{
try
{
server.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
boolean shouldExit()
{
return flag.get();
}
}
The client does exactly that: It opens a connection, and nothing else
import java.io.IOException;
import java.net.Socket;
public class RemoteExitClient
{
public static void main(String[] args)
{
Socket socket = null;
try
{
socket = new Socket("localhost", 1234);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
if (socket != null)
{
try
{
socket.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
}
}
The application is then also very simple:
public class RemoteExitTest
{
public static void main(String[] args)
{
RemoteExitServer e = new RemoteExitServer();
while (!e.shouldExit())
{
System.out.println("Working...");
try
{
Thread.sleep(1000);
}
catch (InterruptedException e1)
{
e1.printStackTrace();
}
}
System.out.println("done");
}
}
(The code could be made even more concise with try-with-resources, but this should not matter here)
You could make use of runtime shutdown hook. That way you won't need to use console input in order to stop the loop. If JVM is being closed normally then shutdown hook thread will run. This thread will wait for the end of current loop iteration. Keep in mind that there are some limitations when using hooks though: https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#addShutdownHook-java.lang.Thread-
import java.util.concurrent.CountDownLatch;
public class Test {
private volatile static CountDownLatch lastIterationLatch = null;
private static boolean stop = false;
public static void main(String [] args) throws Exception {
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
lastIterationLatch = new CountDownLatch(1);
try {
lastIterationLatch.await();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
while(!stop) {
System.out.println("iteration start");
Thread.sleep(200);
System.out.println("processing...");
Thread.sleep(200);
System.out.println("processing...");
Thread.sleep(200);
System.out.println("processing...");
Thread.sleep(200);
System.out.println("iteration end");
if(lastIterationLatch != null) {
stop = true;
lastIterationLatch.countDown();
}
}
}
}
For something quick/dirty, use Signals:
boolean done = false;
// ...
Signal.handle(new Signal("USR1"), new SignalHandler() {
#Override
public void handle(Signal signal) {
// signal triggered ...
done = true;
}
});
// ...
while(!done) { ... }
Then, use kill -USR1 _pid_ to trigger the signal.
You could use a AtomicBoolean as in the test program below.
To suspend just type true into the console to resume type false. The program will never exit.
public class Test2 {
public static void main(String[] args) {
final AtomicBoolean suspended = new AtomicBoolean(false);
new Thread() {
public void run() {
while (true)
{
Scanner sc = new Scanner(System.in);
boolean b = sc.nextBoolean();
suspended.set(b);
}
}
}.start();
while(true){
if(!suspended.get()){
System.out.println("working");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
else{
//System.exit(0) //if you want to exit rather than suspend uncomment.
}
}
}
}
I have rewritten this many times but I could not find a solution to this problem for a while. Some other Class writes gps.log file with lines like:
2014-09-02 10:23:13 35.185604 33.859077
2014-09-02 10:23:18 35.185620 33.859048
I am trying to read the last line of the file and update a text field in the user interface. The Thread below is overdriving the CPU into 85-100%.
I keep the file very tiny (100 lines - < 5KB). I have been working with CSV for a long time, and I think reading this file every 3 seconds should not have this footprint on the CPU. Although I have been reading huge CSV files in the past it is the first time I have this issue now that I try to update the User Interface every couple seconds. Am I doing something wrong with how I am updating the text field? Any ideas?
Thanks for looking.
new Thread(new Runnable() {
public void run() {
while (true) {
Display.getDefault().asyncExec(new Runnable() {
public void run() {
try {
try { Thread.sleep(3000); } catch (Exception e) { }
BufferedReader gpslog = new BufferedReader(new FileReader("log/gps.log"));
String line = "";
String lastLine = "";
int i=0;
while (line != null) {
i++;
lastLine = line;
line = gpslog.readLine();
}
//System.out.println(lastLine);
gpslog.close();
if (lastLine != null) { txtGPSStatus.setText(lastLine); }
//If more than 100 gps entries, flush the file
if (i>100) {
PrintWriter writer = new PrintWriter("log/gps.log");
writer.close();
}
} catch (IOException e1) {
log.error(e1);
}
}
});
}
}
}).start();
Move
try { Thread.sleep(3000); } catch (Exception e) { }
so it is just after
while(true) {
Then you will run, wait 3 secs, run, etc.
You should get a clear idea of what should be done by the background thread and what the UI thread is for!
Executor executor = Executors.newSingleThreadExecutor();
executor.execute(new Runnable() {
public void run() {
try {
while (true) {
updateLog();
Thread.sleep(3000);
}
} catch (InterruptedException ex) {
// restore interruption flag
Thread.currentThread().interrupt();
}
}
});
private void updateLog() {
String lastLine = readLastLogLine();
Display.getDefault().syncExec(new Runnable() {
public void run() {
txtGPSStatus.setText(lastLine);
}
});
}
I tried to write a file monitor which will check the file if a new line is appended,the monitor in fact is a thread which will read the line by a randomaccessfile all the time.
This is the monitor core codes:
public class Monitor {
public static Logger log = Logger.getLogger(Monitor.class);
public static final Monitor instance = new Monitor();
private static final ArrayList<Listener> registers = new ArrayList<Listener>();
private Runnable task = new MonitorTask();
private Thread monitorThread = new Thread(task);
private boolean beStart = true;
private static RandomAccessFile raf = null;
private File monitoredFile = null;
private long lastPos;
public void register(File f, Listener listener) {
this.monitoredFile = f;
registers.add(listener);
monitorThread.start();
}
public void replaceFile(File newFileToBeMonitored) {
this.monitoredFile = newFileToBeMonitored;
// here,how to restart the monitorThread?
}
private void setRandomFile() {
if (!monitoredFile.exists()) {
log.warn("File [" + monitoredFile.getAbsolutePath()
+ "] not exist,will try again after 30 seconds");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setRandomFile();
return;
}
try {
if (raf != null) {
raf.close();
lastPos = 0;
}
raf = new RandomAccessFile(monitoredFile, "r");
log.info("monitor file " + monitoredFile.getAbsolutePath());
} catch (FileNotFoundException e) {
// The file must exist now
} catch (IOException e) {}
}
private void startRead() {
beStart = true;
String line;
while (beStart) {
try {
raf.seek(lastPos);
while ((line = raf.readLine()) != null) {
fireEvent(new FileEvent(monitoredFile.getAbsolutePath(),
line));
}
lastPos = raf.getFilePointer();
} catch (IOException e1) {}
}
}
private void stopRead() {
this.beStart = false;
}
private void fireEvent(FileEvent event) {
for (Listener lis : registers) {
lis.lineAppended(event);
}
}
private class MonitorTask implements Runnable {
#Override
public void run() {
stopRead();
//why putting the resetReandomAccessFile in this thread method is that it will sleep if the file not exist.
setRandomFile();
startRead();
}
}
}
This is some help classes:
public interface Listener {
void lineAppended(FileEvent event);
}
public class FileEvent {
private String line;
private String source;
public FileEvent(String filepath, String addedLine) {
this.line = addedLine;
this.source = filepath;
}
//getter and setter
}
And this is a example to call the monitor:
public class Client implements Listener {
private static File f = new File("D:/ab.txt");
public static void main(String[] args) {
Monitor.instance.register(f, new Client());
System.out.println(" I am done in the main method");
try {
Thread.sleep(5000);
Monitor.instance.replaceFile(new File("D:/new.txt"));
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}
}
#Override
public void lineAppended(FileEvent event) {
String line = event.getLine();
if (line.length() <= 0)
return;
System.err.println("found in listener:" + line + ":" + line.length());
}
}
Now,my probelm is the code work well if I just call:
Monitor.instance.register(file,listener);
This will monitor the file for line appending,and will notify the listener.
However it does not work when I call the :
Monitor.instance.replaceFile(anotherfile);
This means I want to monitor another file rather than before.
So in my Monitor I have to restart the thread,how to make it?
I have tried the:
monitorThread.interruppt();
It does not wrok.
Anyone can fix it for me or tell me how to do ?
Thanks.
Before I ask,I have googling the "restart java thread",so I know one can not restart a dead thread,but my thread does not return,so I think it can be restarted.
You don't restart a Thread, instead you create a new one each time you want to start a thread.
A better alternative may be to use Executors.newCachedThreadPool() which gives you a pool of thread which will be started/recycle for you.
BTW: You are using recursion rather than a loop to poll if the file exists. Using recursion can mean if you wait too long it will throw a StackOverflowError. IMHO you shouldn't wait at all, the polling thread should repeatedly attempt to open the file until it is told to stop (or the file appears)
Your current implementation also means if the file is replaced, you will have to reopen the file in the background thread anyway.
Instead of explaining, I just coded up a skeleton example. I did not test it terribly well, but it may be of some use.
In order to monitor a(nother) file, just create a new Monitor, passing it a ScheduledExecutorService. Starting and stopping monitoring is straightforward. You can (should) reuse the same executor for multiple monitors.
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public interface Event
{
}
public interface Listener
{
void handle(Event event);
}
public class Monitor
{
private static final int CHECK_EVERY_SECONDS = 10;
private static final int RECHECK_AFTER_IF_NOT_EXISTS_SECONDS = 30;
private File file;
private ScheduledExecutorService executor;
private boolean active;
private List<Listener> listeners;
public Monitor(File file, ScheduledExecutorService executor)
{
super();
this.file = file;
this.executor = executor;
listeners = new ArrayList<Listener>();
}
public synchronized void start()
{
if (active)
{
return;
}
active = true;
executor.execute(new Runnable()
{
public void run()
{
synchronized (Monitor.this)
{
if (!active)
{
System.out.println("not active");
return;
}
}
if (!file.exists())
{
System.out.println("does not exist, rescheduled");
executor.schedule(this, RECHECK_AFTER_IF_NOT_EXISTS_SECONDS, TimeUnit.SECONDS);
return;
}
Event event = doStuff(file);
System.out.println("generated " + event);
updateListeners(event);
System.out.println("updated listeners and rescheduled");
executor.schedule(this, CHECK_EVERY_SECONDS, TimeUnit.SECONDS);
}
});
}
private Event doStuff(final File file)
{
return new Event()
{
public String toString()
{
return "event for " + file;
}
};
}
public synchronized void stop()
{
active = false;
}
public void addListener(Listener listener)
{
synchronized (listeners)
{
listeners.add(listener);
}
}
public void removeListener(Listener listener)
{
synchronized (listeners)
{
listeners.remove(listener);
}
}
private void updateListeners(Event event)
{
synchronized (listeners)
{
for (Listener listener : listeners)
{
listener.handle(event);
}
}
}
public static void main(String[] args) throws IOException
{
ScheduledExecutorService executor = Executors.newScheduledThreadPool(4);
File file = new File("test.png");
Monitor monitor = new Monitor(file, executor);
monitor.addListener(new Listener()
{
public void handle(Event event)
{
System.out.println("handling " + event);
}
});
monitor.start();
System.out.println("started...");
System.in.read();
monitor.stop();
System.out.println("done");
executor.shutdown();
}
}
See this post How to start/stop/restart a thread in Java?
I assume you answered your question
one can not restart a dead thread
This link may be helpful to you How to restart thread in java?
A thread in Java cannot be re-started. Every time you need to restart the thread you must make a new one.
That said, you might want to look at:
private void setRandomFile() {
if (!monitoredFile.exists()) {
log.warn("File [" + monitoredFile.getAbsolutePath()
+ "] not exist,will try again after 30 seconds");
try {
Thread.sleep(30 * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
setRandomFile();
return;
}
// ....
}
Here you sleep for 30 seconds if the file does not exist, then recursively call the same function. Now, I don't know what business requirements you have, but if this recursion ran long enough you will run out of stack space. Perhaps you will be better served with a while loop or even better, a little synchronisation like a Semaphore.