I have tried nearly everything to get this work, but it seems that I don't get the right direction.
Here is the actual Situation: I use JSF2.2 with GlashFish7 under Netbeans7.3. My JSF application should create an second thread to run(Asynchronously) an endless loop. In this endless loop I use the WatchService (NIO) to check a specific folder for changes.
The WatchService function works fine in a single thread driven jsf page. But I will do other stuff and during the loop, so I need this methode async, but i'm not able to run it in a seperad thread.
Here is my java class:
#Stateless
public class NewFile {
#Asynchronous
public void showNewFile() throws IOException{
WatchService watchService = FileSystems.getDefault().newWatchService();
WatchKey watchKey = Paths.get("/home/user/input").register(watchService,new WatchEvent.Kind<?>[] { ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE });
while (true) {
try {
watchKey = watchService.take();
} catch (InterruptedException ex) {
System.out.println("InterruptedException: " + ex);
}
for (WatchEvent<?> watchEvent : watchKey.pollEvents()) {
System.out.println(watchEvent.kind() + " " + ((Path) watchEvent.context()));
}
watchKey.reset();
}
}
}
In this class I call the methode:
#Named("startWatcher")
public class StartWatcher {
private NewFile newFile;
public void runSearcher() throws IOException{
newFile.showNewFile();
}
}
and the relevant section from thy index.xhtml
<h:commandButton actionListener="#{startWatcher.runSearcher()}" value="test"/>
I hope you understand my problem, I know my english isn't very good. i'm looking forward to receive a hint what i'm doing wrong.
Typically, you should start a watchservice thread when your GlassFish domain is deployed by defining a #WebListener, for example:
import static java.nio.file.StandardWatchEventKinds.*;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
#WebListener
public class MyServletContextListener implements ServletContextListener {
private final NewFileRunner runner = new NewFileRunner();
#Override
public void contextInitialized(ServletContextEvent sce) {
runner.startThread();
}
#Override
public void contextDestroyed(ServletContextEvent arg0) {
runner.stopThread();
}
class NewFileRunner implements Runnable {
private volatile Thread thread;
private final WatchService watchService;
public NewFileRunner() {
watchService = FileSystems.getDefault().newWatchService();
Paths.get("/home/user/input").register(watchService, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
}
/**
* Start a worker thread to listen for directory changes.
*/
public void startThread() {
thread = new Thread(this);
thread.start();
}
/**
* Flag worker thread to stop gracefully.
*/
public void stopThread() {
if (thread != null) {
Thread runningThread = thread;
thread = null;
runningThread.interrupt();
}
}
#Override
public void run() {
Thread runningThread = Thread.currentThread();
while (runningThread == thread) {
WatchKey watchKey = null;
try {
watchKey = watchService.take();
if (watchKey != null) {
for (WatchEvent<?> watchEvent : watchKey.pollEvents()) {
System.out.println(watchEvent.kind() + " " + ((Path) watchEvent.context()));
}
watchKey.reset();
}
} catch (InterruptedException e) {
e = null;
}
}
}
}
}
Of course, I'd refactor this out into multiple classes, but this will point you in the right direction.
Related
I am trying to build a simple Echo Service using Java NIO 2 (which is based on Proactor Pattern).
In the simplest implementation, we have 4 main components; ProactorInitiator, AcceptConnectionHandler, ReadConnectionHandler and WriteConnectionHandler.
Following is my sample code.
ProactorInitiator.java
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.AsynchronousServerSocketChannel;
public class ProactorInitiator {
static int ASYNC_SERVER_PORT = 4333;
public void initiateProactiveServer(int port)
throws IOException {
final AsynchronousServerSocketChannel listener =
AsynchronousServerSocketChannel.open().bind(
new InetSocketAddress(port));
AcceptCompletionHandler acceptCompletionHandler =
new AcceptCompletionHandler(listener);
SessionState state = new SessionState();
listener.accept(state, acceptCompletionHandler);
System.out.println("Proactor Initiator Running on "+Thread.currentThread().getName());
}
public static void main(String[] args) {
try {
System.out.println("Async server listening on port : " +
ASYNC_SERVER_PORT);
new ProactorInitiator().initiateProactiveServer(
ASYNC_SERVER_PORT);
} catch (IOException e) {
e.printStackTrace();
}
// Sleep indefinitely since otherwise the JVM would terminate
while (true) {
try {
Thread.sleep(Long.MAX_VALUE);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
AcceptCompletionHandler.java
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousServerSocketChannel;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class AcceptCompletionHandler
implements
CompletionHandler<AsynchronousSocketChannel, SessionState> {
private AsynchronousServerSocketChannel listener;
public AcceptCompletionHandler(
AsynchronousServerSocketChannel listener) {
this.listener = listener;
}
#Override
public void completed(AsynchronousSocketChannel socketChannel,
SessionState sessionState) {
System.out.println("Accept Handler running on "+Thread.currentThread().getName());
// accept the next connection
SessionState newSessionState = new SessionState();
listener.accept(newSessionState, this);
// handle this connection
ByteBuffer inputBuffer = ByteBuffer.allocate(2048);
ReadCompletionHandler readCompletionHandler =
new ReadCompletionHandler(socketChannel, inputBuffer);
socketChannel.read(
inputBuffer, sessionState, readCompletionHandler);
}
#Override
public void failed(Throwable exc, SessionState sessionState) {
// Handle connection failure...
}
}
ReadCompletionHandler.java
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class ReadCompletionHandler implements
CompletionHandler<Integer, SessionState> {
private AsynchronousSocketChannel socketChannel;
private ByteBuffer inputBuffer;
public ReadCompletionHandler(
AsynchronousSocketChannel socketChannel,
ByteBuffer inputBuffer) {
this.socketChannel = socketChannel;
this.inputBuffer = inputBuffer;
}
#Override
public void completed(
Integer bytesRead, SessionState sessionState) {
System.out.println("Read Handler running on "+Thread.currentThread().getName());
byte[] buffer = new byte[bytesRead];
inputBuffer.rewind();
// Rewind the input buffer to read from the beginning
inputBuffer.get(buffer);
String message = new String(buffer);
// System.out.println("Received message from client : " + message);
// message = GetRequestParser.getHTTPRequest(message, "200 OK");
// Echo the message back to client
WriteCompletionHandler writeCompletionHandler =
new WriteCompletionHandler(socketChannel);
ByteBuffer outputBuffer = ByteBuffer.wrap(message.getBytes());
socketChannel.write(
outputBuffer, sessionState, writeCompletionHandler);
}
#Override
public void failed(Throwable exc, SessionState attachment) {
//Handle read failure.....
}
}
WriteCompletionHandler.java
import java.io.IOException;
import java.nio.channels.AsynchronousSocketChannel;
import java.nio.channels.CompletionHandler;
public class WriteCompletionHandler implements
CompletionHandler<Integer, SessionState> {
private AsynchronousSocketChannel socketChannel;
public WriteCompletionHandler(
AsynchronousSocketChannel socketChannel) {
this.socketChannel = socketChannel;
}
#Override
public void completed(
Integer bytesWritten, SessionState attachment) {
try {
System.out.println("Write Handler running on "+Thread.currentThread().getName());
System.out.println("\n");
socketChannel.close();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void failed(Throwable exc, SessionState attachment) {
// Handle write failure.....
}
}
SessionState.java
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class SessionState {
private Map<String, String> sessionProps =
new ConcurrentHashMap<>();
public String getProperty(String key) {
return sessionProps.get(key);
}
public void setProperty(String key, String value) {
sessionProps.put(key, value);
}
}
In order to check the threading behaviour, I print the thread on which each handler runs to 'sys.out'.
Following are the different results I got, for many requests which are sent to the server one after the other.
Request 1
Accept Handler running on Thread-4
Read Handler running on Thread-4
Write Handler running on Thread-4
Request 2
Accept Handler running on Thread-4
Read Handler running on Thread-2
Write Handler running on Thread-2
Request 3
Accept Handler running on Thread-5
Read Handler running on Thread-3
Write Handler running on Thread-3
According to the above results, it seems like, for different requests, the server uses different threads. Also, both Read Handler and Write Handler are run on the same thread for a given request.
Can someone explain this result? As how handlers are scheduled on different threads?
As seen in your results for Thread.getCurrentThread().getName() for each Completion handler, in NIO2 (proactor pattern) the thread allocation for different Completion handlers is not specified and seems random. So, the best practice is not to assume any thread behaviour.
For the sake of completeness, I am adding the thread behaviour of NIO, in the following.
In NIO, each activity (be it socket accept, read or write) is run in a single thread (in which the selector loop runs.)
I am using Watcher service to monitor a directory in Windows. If a change is done to a file, I'd like to write a timestamp to the same file. Of course, this is yet another modification to the directory and watcher then processes the event again. Is there a way to suspend watcher so I can update the file, then restart it so it can wait for the next event?
Perhaps there's a better suggestion???
package net.codejava.io;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.sql.Timestamp;
import java.util.Date;
public class DirectoryWatchDemo {
static String logfile = "C:/Temp/log.txt";
volatile static boolean suspended = false;
public static void main(String[] args) {
try {
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("C:/Temp/");
dir.register(watcher, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
// System.out.println("Watch Service registered for dir: " +
// dir.getFileName());
while (true) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException ex) {
return;
}
if (!suspended) {
resume();
} else {
updateFile();
suspend();
}
if (key.isValid())
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
#SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path fileName = ev.context();
System.out.println(kind.name() + ": " + fileName);
}
boolean valid = key.reset();
if (!valid) {
System.out.println("Watch service invalid");
break;
}
}
} catch (IOException ex) {
System.err.println(ex);
}
}
private static void suspend() {
suspended = false;
}
private static void resume() {
suspended = true;
}
public static void updateFile() {
try {
File outfile = new File(logfile);
if (!outfile.exists()) {
System.out.println("No file exists...writing a new file");
outfile.createNewFile();
}
FileWriter fw = new FileWriter(outfile.getAbsoluteFile(), true);
BufferedWriter bw = new BufferedWriter(fw);
bw.write("TimeStamp: " + new Timestamp(new java.util.Date().getTime()).toString() + "\r\n");
bw.flush();
bw.close();
System.out.println("done");
} catch (IOException e) {
e.printStackTrace();
}
}
}
You need to interrupt the thread that is using the watch service, because that's the only way you get out of watcher.take(). Example with old-school threads:
public void start() {
thread = new Thread(directoryWatchDemo::watch);
thread.start();
}
public void pause() throws InterruptedException {
if (thread.isAlive()) {
thread.interrupt();
thread.join();
}
}
See ExecutorService like Executors.newSingleThreadExecutor() and Future.cancel() for more flexibility.
Alternatively, you can take() all events and ignore them based on a volatile boolean.
Sorry, this question has probably been asked before, but I couldn't find any with an answer in the context that applies specifically enough to my problem for me to apply the solution.
Anyways, I'm working on a program that uses a file. When that file is updated, I want it to replace the File variable with the current one. I set up a main class that will work with the file, then I set up another class with a different thread that listens for the file update. When the file is updated, I want the variable in the main class to be updated.
That means that the update listener class has to have the instance of the main class, but when I try to send it during initiation of the update listener class, a warning says the main class cannot be referenced from a static context.
Here's the code:
Main Class
package me.xeyler;
import com.sun.media.jfxmedia.logging.Logger;
import javax.swing.*;
import java.awt.Color;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.*;
/**
* Created by Brigham on 10/19/2016.
*/
public class ViewerMain {
static FileHandler fileHandler;
static File skinFile;
public static void main(String[] args) {
boolean bool = false;
fileHandler = new FileHandler(this);
fileHandler.start();
while(true) {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(bool);
}
}
public void setSkinFile(File skinFile) {
this.skinFile = skinFile;
}
}
File Listener Class
package me.xeyler;
import com.sun.media.jfxmedia.logging.Logger;
import javax.swing.*;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.nio.file.*;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;
/**
* Created by Brigham on 10/19/2016.
*/
public class FileHandler implements Runnable {
private Thread fileThread;
private String threadName;
WatchService watcher = null;
private ViewerMain main;
public FileHandler(ViewerMain main) {
this.main = main;
this.threadName = "FileThread";
}
public void watchFile(Path path) {
}
public void watchFile(File file) {
watchFile(Paths.get(file.getPath()));
}
public void close() {
try {
watcher.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public void start () {
if (fileThread == null) {
System.out.println("Starting new thread...");
fileThread = new Thread (this, threadName);
fileThread.start();
System.out.println("Started thread: " + threadName);
}
}
#Override
public void run() {
System.out.println("Running thread...");
Path dir = Paths.get(System.getProperty("user.home"),"documents");
try {
watcher = FileSystems.getDefault().newWatchService();
WatchKey key = dir.register(watcher,
ENTRY_MODIFY);
} catch (IOException x) {
x.printStackTrace();
}
for (;;) {
// wait for key to be signaled
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException x) {
return;
}
for (WatchEvent<?> event: key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// The filename is the
// context of the event.
WatchEvent<Path> ev = (WatchEvent<Path>)event;
Path filename = ev.context();
if (filename.endsWith("text.txt")) {
System.out.println("File has changed");
//TODO: Update File variable in ViewerMain
main.setSkinFile(filename.toFile());
}
}
// Reset the key -- this step is critical if you want to
// receive further watch events. If the key is no longer valid,
// the directory is inaccessible so exit the loop.
boolean valid = key.reset();
if (!valid) {
// TODO: Handle inaccessible directory
break;
}
}
}
}
I suspect the answer is really obvious, but thanks for the patience!
If I understand correctly, you need an instance of the ViewerMain class.
this cannot be applied in a static context.
public static void main(String[] args) {
ViewerMain viewer = new ViewerMain(); // an instance
fileHandler = new FileHandler(viewer);
Same for skinFile
public File skinFile; // Remove static
public void setSkinFile(File skinFile) {
this.skinFile = skinFile;
}
You can not do this:
public void setSkinFile(File skinFile) {
this.skinFile = skinFile;
}
since skinFile is static, it would be better if you set that property as public static File skinFile; and then you accesed the property directly from the FileHandler:
ViewerMain.skinFile = filename.toFile()
given that it is a static property you dont need an instance of the class to access it, you can use the class directly.
I am starting a Thread from ServletContextListener when the context is initialized and trying to stop it when the context is destroyed. The class is:
public enum BlinkLedTask {
INSTANCE;
private Logger logger = RpiLogger.getLogger(getClass());
private Task task;
private ExecutorService service;
private BlinkLedTask() {
}
public void run(String[] frequency) {
stop();
task = new Task(frequency);
service = Executors.newSingleThreadExecutor(RpiThreadFactory.INSTANCE);
service.execute(task);
}
public void stop() {
if(Objects.isNull(task) || Objects.isNull(service)) {
return;
}
try {
task.terminate();
service.shutdownNow();
} catch (Exception cause) {
logger.error(cause.getMessage(), cause);
}
}
private static class Task implements Runnable {
private volatile boolean running = true;
private String[] frequency;
private volatile Logger logger = RpiLogger.getLogger(getClass());
private Task(String[] frequency) {
this.frequency = frequency;
}
#Override
public void run() {
while(running && !Thread.interrupted()) {
try {
resetLed();
blinkLed();
} catch (Throwable cause) {
logger.error(cause.getMessage(), cause);
running = false;
try {
resetLed();
} catch (Throwable ignore) {
}
}
}
}
private void resetLed() throws Exception {
executeScript(Script.BLINK_LED_RESET);
}
private void blinkLed() throws Exception {
executeScript(Script.BLINK_LED, new String[]{frequency[0], frequency[1], frequency[2]});
}
private void executeScript(Script script, String... args) {
ScriptExecutor scriptExecutor = new ScriptExecutor(ScriptExecutor.BASH, script);
scriptExecutor.execute(true, args);
}
private void terminate() {
logger.info("Stopping - " + Thread.currentThread().getName());
running = false;
}
}
}
This a Singleton and the which runs a shell script until it is stopped. This class can be called from anywhere, so I need to stop the thread, if there is any currently executing the shell script, before creating a new Thread.
For testing purpose I have executed the run() method of this class when the context is initialized and called the stop() at the time of destroy.
I have redeploy the war file after removing code run(), I was expecting that the stop() will terminate the task, but it didn't.
I also have tried a different implementation of the run() and stop():
public void run(String[] frequency) {
stop();
task = new Task(frequency);
Thread thread = RpiThreadFactory.INSTANCE.newThread(task);
tasks.add(ImmutablePair.of(thread, task));
thread.start();
}
public void stop() {
for(ImmutablePair<Thread, Task> pair : tasks) {
try {
pair.right.terminate();
pair.left.join();
} catch (Exception ex) {
}
}
}
Here the tasks is private ArrayList<ImmutablePair<Thread, Task>> tasks = new ArrayList<ImmutablePair<Thread,Task>>();. The ImmutablePair belongs to commons-lang3. But I received java.util.ConcurrentModificationException on the iteration of the enhanced for loop. The cause I don't know.
Update
When the server get shutdown the stop() is working as expected. I am using Jetty.
Update
RpiThreadFactory:
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.log4j.Logger;
import com.edfx.rpi.app.utils.logger.RpiLogger;
public enum RpiThreadFactory implements ThreadFactory {
INSTANCE;
private final AtomicInteger poolNumber = new AtomicInteger(1);
private final Logger logger = RpiLogger.getLogger(getClass());
private final ThreadGroup threadGroup;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
private RpiThreadFactory() {
SecurityManager securityManager = System.getSecurityManager();
threadGroup = (securityManager != null) ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = "RpiPool-" + poolNumber.getAndIncrement() + "-Thread-";
}
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(threadGroup, runnable, namePrefix + threadNumber.getAndIncrement(), 0);
thread.setPriority(Thread.NORM_PRIORITY);
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
public void uncaughtException(Thread thread, Throwable cause) {
logger.error(cause.getMessage(), cause);
}
});
return thread;
}
}
ScriptExecutor:
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import com.edfx.rpi.app.utils.logger.RpiLogger;
public class ScriptExecutor {
private static final Logger LOGGER = RpiLogger.getLogger(ScriptExecutor.class);
public static final String BASH = "/bin/bash";
private Script script;
private Process process;
private String output;
private int exitValue;
public ScriptExecutor(Script script) {
this.script = script;
}
public void execute(boolean destroyProcess, String... args) throws ScriptNotExistException {
if(!script.exists()) {
throw new ScriptNotExistException(script.getScriptName() + " does not exists.");
}
try {
List<String> commands = new ArrayList<>();
commands.add(BASH);
commands.add(script.getAbsoultePath());
if(Objects.nonNull(args)) {
commands.addAll(Arrays.asList(args));
}
StringBuilder builder = new StringBuilder("Executing script: ");
builder.append(script.getScriptName());
if(Objects.nonNull(args) && args.length > 0) {
builder.append(" with parameters: ");
builder.append(StringUtils.join(args, " "));
}
LOGGER.info(builder.toString());
ProcessBuilder processBuilder = new ProcessBuilder(commands.toArray(new String[commands.size()]));
process = processBuilder.start();
StringBuilder outputBuilder = new StringBuilder();
InputStream inputStream = process.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = StringUtils.EMPTY;
while ((line = bufferedReader.readLine()) != null) {
outputBuilder.append(line);
outputBuilder.append("\n");
}
process.waitFor();
exitValue = process.exitValue();
LOGGER.info("Process for: " + script.getScriptName() + " is executed. Exit value: " + exitValue);
if(destroyProcess) {
destroyProcess();
}
output = outputBuilder.toString();
} catch (Exception cause) {
throw new ScriptExecutionException(cause);
}
}
public String getOutput() {
return output;
}
public int getExitValue() {
return exitValue;
}
public void destroyProcess() {
if(Objects.nonNull(process)) {
LOGGER.info("Process for: " + script.getScriptName() + " is destroyed.");
process.destroy();
}
}
}
Purpose
This is a web application running in Jetty web container. The server is installed in an embedded hardware java enabled. How this hardware has a LED attached to it. The application accepts external request, which can be REST and start-stops the LED. So the LED can start blinking for any request; but it serves only one request at a time.
This is why I have the stop which stops previously running process, if there is any. The stop works for normal condition.
But I saw that while the LED is blinking and I did a deployment without stopping the server the running thread doesn't stops. If I stop the server and did the deployment and the start again, the running thread kills at this time.
The thread loops in the while and executes a Process to the native. This Process is an one time job, so this Process is not making the thread to get killed.
To reproduce the issue what I did I created the thread when the context is initialized and tried to kill it when it is destroyed. Now if I write something in the contextDestroyed I can see them get executed.
I don't understand why stopping the server kills the thread not when I redeploy.
You should call process.destroy() on instance of Process returned by processBuilder.start(). Actually what you do when calling BlinkLedTask.terminate() is just setting some flag. You should at this point call process.destroy().
Below I present an example how you can rewrite this.It does not involve your class ScriptExecutor (of course you can move your logic there and return instance of process to BlinkLedTask when calling blinkLed()).
The main difference here is that I'm keeping the reference to instance of Process in field blinkLedProcess and when terminate() is called I'm directly calling process.destroy() to destroy the process.
You wrote that "When the server get shutdown the stop() is working as expected. I am using Jetty." Yes indeed. This is because by calling processBuilder.start(); you create subprocess of your main jetty process. When you kill jetty all its subproceses are also killed. If you don't kill jetty you need to manually kill the subprocess by calling destroy() method.
It should be something like:
public enum BlinkLedTask {
(...)
private Process resetLedProcess;
private Process blinkLedProcess;
(...)
private void blinkLed() throws Exception {
String[] args = new String[] { frequency[0], frequency[1], frequency[2] };
List<String> commands = new ArrayList<>();
//commands.add(BASH);
commands.add(script.getAbsoultePath());
if (Objects.nonNull(args)) {
commands.addAll(Arrays.asList(args));
}
StringBuilder builder = new StringBuilder("Executing script: ");
builder.append(script.getAbsoultePath());
if (Objects.nonNull(args) && (args.length > 0)) {
builder.append(" with parameters: ");
builder.append(StringUtils.join(args, " "));
}
ProcessBuilder processBuilder = new ProcessBuilder(commands.toArray(new String[commands.size()]));
blinkLedProcess = processBuilder.start();
StringBuilder outputBuilder = new StringBuilder();
InputStream inputStream = blinkLedProcess.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String line = StringUtils.EMPTY;
while ((line = bufferedReader.readLine()) != null) {
outputBuilder.append(line);
outputBuilder.append("\n");
}
blinkLedProcess.waitFor();
int exitValue = blinkLedProcess.exitValue();
System.out.println(
"Process for: " + Script.BLINK_LED.getAbsoultePath() + " is executed. Exit value: " + exitValue);
}
(...)
private void terminate() {
System.out.println("Stopping - " + Thread.currentThread().getName());
running = false;
if (resetLedProcess != null) {
resetLedProcess.destroy();
System.out.println("Destroyed reset process");
}
if (blinkLedProcess != null) {
blinkLedProcess.destroy();
System.out.println("Destroyed blink process");
}
}
(...)
}
First of all, you should use awaitTermination to wait after calling shutdownNow.
shutdownNow will interrupt your thread. Are you sure ScriptExecutordoes not surpress interrupts?
This might actually be the cause of this.
Also doing this with SchedulerService seems redundant since you are only using one thread.
You could start a new thread which you set to a daemon thread (See What is Daemon thread in Java?) and it will close automatically when your program exits.
What exactly are you trying to achieve? If its a single thread that runs during the lifetime of your web app then I would just write your own context listener with a thread like this...
public class MyContextListener implements ServletContextListener {
Thread myThread;
Task myTask;
#Override
public void contextInitialized(ServletContextEvent sce) {
myTask = new Task();
myThread = new Thread(myTask);
myThread.start();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
myTask.terminate();
myThread.interrupt();
myThread.join();
}
}
I'm wondering exactly what it is you want to achieve. Do you only want a single thread running a series of scripts? Or are you hoping, at some point in the future, to have a multi-threaded application?
Any reason for the servlet context? Could you just run this is a straight forward java application?
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.