I'm currently developing a Vaadin-based program in Java which extracts documents from Domino databases and writes them to a MongoDB collection. The program works perfectly but has one small flaw:
Currently i've found no way to stop the program other then send "KILL" to the process. My shutdown hook/signal handler is totally ignored. I've narrowed down the problem to a single line of code:
NotesThread.sinitThread();
When i remove this line, the hook works perfectly and my program is shutdown properly. When the line is inserted, then the hook is never called.
Here is some example code:
private boolean running = true;
...
#Override
public void run() {
try {
NotesGC.runWithAutoGC(() -> {
NotesThread.sinitThread() // --> "Kills" all signal handling
Session session = NotesFactory.createSession();
while (running) {
Thread.sleep(1000);
System.out.println("Running ...");
}
session.recycle();
return null;
});
} catch (Exception e) {
} finally {
NotesThread.stermThread();
}
}
public void kill() {
System.out.println("Killed!");
this.running = false;
}
...
Signal.handle(new Signal("TERM"), sig -> runner.kill()); // Signal handler from main-method
I've asks friends and colleagues and nobody ever had the same problem.
NotesGC.runWithAutoGC
As I see you are using Domino JNA, a side project.
It use the domino CAPI.
It's open source: https://github.com/klehmann/domino-jna
You can create issue ticket or ask question.
BTW, func "runWithAutoGC" call initThread() in his body.
This link for source code: https://github.com/klehmann/domino-jna/blob/master/domino-jna/src/main/java/com/mindoo/domino/jna/gc/NotesGC.java
Related
I have been developing a project for my client. It is a type of social media application.
The database is firebase and it is written in java and some kotlin.
The debug apk was fine.
I have tested it and also used it but when I try to release the app on playstore, I have to generate a signed apk, but the signed apk is not like debug apk it has many bugs.
I don’t know what’s happening, please help me.
Thank you in advance.
I had similar problem. Only now I discovered that the standard intellIj and Android Studio mode are debug mode.
I reviewed the threads of my application on release mode, and replaced the threads of type 1 for threads of type 2:
Threads Type 1:
class TimeCheckURL extends TimerTask
{
#RequiresApi(api = Build.VERSION_CODES.M)
public void run()
{
new Thread(new Runnable() {
#Override
public void run() {
Data = null;
Data = new JsonTask().execute(urlBase);
threadEnd = true;
}
}).start();
}
}
Threads Type 2:
Thread threadReadHexBsvTx = new Thread(new Runnable() {
#Override
public void run() {
try {
Data = JsonTask(urlBase);
} catch (Exception e) {
e.printStackTrace();
}
}
});
private void renewThread()
{
threadReadHexBsvTx = new Thread(new Runnable() {
#Override
public void run() {
try {
Data = JsonTask(urlBase);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
To monitor if threads type 1 were still alive I used the following approach;
while (!threadEnd) {
//do something
}
It worked very well when I unknowingly developed under Build Variant Debug, however when I put it on playstore I discovered that there was a release variant, and that my application did not worked properly on release mode.
It happened because everytime the processes using threads type 1 were called for the second time, the thread did not updated "threadEnd" and it looped forever.
I discovered that I could monitor the state of thread type 2 using this aproach.
while (bsvTX.threadBroadCast.isAlive()) {
//do something
}
This solved the problem in my application. However, I could not use approach of thread type 2 on thread type 1.
It might sound very noob, but I also discovered that for threads type 2 to work the second time the processes called them, they had to be renewed, that is why I use the method renewThread().
I need to make a program, which can be executed in single instance. I tried to create a temporary file and delete it before exit program.
public static boolean isLocked() {
File f = new File("lock.txt");
return f.exists();
}
public static void lock() {
String fname = "lock.txt";
File f = new File(fname);
try {
f.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void unlock() {
File f = new File("lock.txt");
f.delete();
}
In frame
private void initialize() {
lock();
}
private void setFrameHandler() {
frame.addWindowListener(new java.awt.event.WindowAdapter() {
#Override
public void windowClosing(java.awt.event.WindowEvent windowEvent) {
unlock();
}
});
}
Problem occurs if program is finished with emergency (e.g. electricity cuts). File does not remove, and running a new instance is impossible.
How to make a reliable single-instance verification?
You could check for another instance of the program at startup using the GetProcesses method as described here
But that only works depending on the scenario you have (might not see all processes of other users)
Another thing you could do is simply checking, if a specific file is locked via File.Open
File.Open ("path.lock", FileMode.OpenOrCreate, FileAccess.ReadWrite);
As long as you keep the resulting FileStream open in your program no other program can open the file in that mode either. This is basically how Unix lock files work too. Of course you have to catch an IOException (hinting you to a locked file).
Disclaimer: I did not try that code out so please check if I gave you the right parameters.
Edit: You could also check out this Code-Project article on how to do it with the win32 API
Another attempt using windows messaging has been done here
A simple approach to this on a single machine is to write a 'PID file', which is literally a file containing the operating system's ID of the process currently running. You create this when you start your "critical" work, and remove it on successful completion.
Since it is unlikely that the process would be started again with the same PID, you can simply check to see if the PID file already exists, and if so, if that process is still running.
I'd been googling around for a way for me to send in command to a running Java program, but most of the post suggested to implement listener or wrap the program with a Jetty (or other server) implementation.
Is there a way to do this without adding additional dependencies?
The scenario is, i have a Java program which will be running indefinitely, and which will spawn a few running threads. I would like to be able to run a script to stop it, when it needs to be shut down, somewhat like the shutdown script servers tend to have. This will allow me to handle the shutdown process in the program. The program runs in a linux environment.
Thank you.
Implemented the shutdown hook and so far it looks good. The implementation codes:
final Thread mainThread = Thread.currentThread();
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() {
logger.info("Shut down detected. Setting isRunning to false.");
if(processors != null && !processors.isEmpty()){
for (Iterator<IProcessor> iterator = processors.iterator(); iterator.hasNext();) {
IProcessor iProcessor = (IProcessor) iterator.next();
iProcessor.setIsRunning(false);
try {
iProcessor.closeConnection();
} catch (SQLException e1) {
logger.error("Error closing connection",e1);
}
}
}
try {
mainThread.join();
} catch (InterruptedException e) {
logger.error("Error while joining mainthread to shutdown hook",e);
}
}
});
Thanks for the suggestion.
A program that I've developed is crashing the JVM occasionally due to this bug: http://bugs.java.com/bugdatabase/view_bug.do?bug_id=8029516. Unfortunately the bug has not been resolved by Oracle and the bug report says that there are no known workarounds.
I've tried to modify the example code from the bug report by calling .register(sWatchService, eventKinds) in the KeyWatcher thread instead, by adding all pending register request to a list that I loop through in the KeyWatcher thread but it's still crashing. I'm guessing this just had the same effect as synchronizing on sWatchService (like the submitter of the bug report tried).
Can you think of any way to get around this?
From comments:
It appears that we have an issue with I/O cancellation when there is a pending ReadDirectoryChangesW outstanding.
The statement and example code indicate that the bug is triggered when:
There is a pending event that has not been consumed (it may or may not be visible to WatchService.poll() or WatchService.take())
WatchKey.cancel() is called on the key
This is a nasty bug with no universal workaround. The approach depends on the specifics of your application. Consider pooling watches to a single place so you don't need to call WatchKey.cancel(). If at one point the pool becomes too large, close the entire WatchService and start over. Something similar to.
public class FileWatcerService {
static Kind<?>[] allEvents = new Kind<?>[] {
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_DELETE,
StandardWatchEventKinds.ENTRY_MODIFY
};
WatchService ws;
// Keep track of paths and registered listeners
Map<String, List<FileChangeListener>> listeners = new ConcurrentHashMap<String, List<FileChangeListener>>();
Map<WatchKey, String> keys = new ConcurrentHashMap<WatchKey, String>();
boolean toStop = false;
public interface FileChangeListener {
void onChange();
}
public void addFileChangeListener(String path, FileChangeListener l) {
if(!listeners.containsKey(path)) {
listeners.put(path, new ArrayList<FileChangeListener>());
keys.put(Paths.get(path).register(ws, allEvents), path);
}
listeners.get(path).add(l);
}
public void removeFileChangeListener(String path, FileChangeListener l) {
if(listeners.containsKey(path))
listeners.get(path).remove(l);
}
public void start() {
ws = FileSystems.getDefault().newWatchService();
new Thread(new Runnable() {
public void run() {
while(!toStop) {
WatchKey key = ws.take();
for(FileChangeListener l: listeners.get(keys.get(key)))
l.onChange();
}
}
}).start();
}
public void stop() {
toStop = true;
ws.close();
}
}
I've managed to create a workaround though it's somewhat ugly.
The bug is in JDK method WindowsWatchKey.invalidate() that releases native buffer while the subsequent calls may still access it. This one-liner fixes the problem by delaying buffer clean-up until GC.
Here is a compiled patch to JDK. In order to apply it add the following Java command-line flag:
-Xbootclasspath/p:jdk-8029516-patch.jar
If patching JDK is not an option in your case, there is still a workaround on the application level. It relies on the knowledge of Windows WatchService internal implementation.
public class JDK_8029516 {
private static final Field bufferField = getField("sun.nio.fs.WindowsWatchService$WindowsWatchKey", "buffer");
private static final Field cleanerField = getField("sun.nio.fs.NativeBuffer", "cleaner");
private static final Cleaner dummyCleaner = Cleaner.create(Thread.class, new Thread());
private static Field getField(String className, String fieldName) {
try {
Field f = Class.forName(className).getDeclaredField(fieldName);
f.setAccessible(true);
return f;
} catch (Exception e) {
throw new IllegalStateException(e);
}
}
public static void patch(WatchKey key) {
try {
cleanerField.set(bufferField.get(key), dummyCleaner);
} catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
}
Call JDK_8029516.patch(watchKey) right after the key is registred, and it will prevent watchKey.cancel() from releasing the native buffer prematurely.
You might not be able to work around the problem itself but you could deal with the error and handle it. I don't know your specific situation but I could imagine the biggest issue is the crash of the whole JVM. Putting all in a try block does not work because you cannot catch a JVM crash.
Not knowing more about your project makes it difficult to suggest a good/acceptable solution, but maybe this could be an option: Do all the file watching stuff in a separate JVM process. From your main process start a new JVM (e.g. using ProcessBuilder.start()). When the process terminates (i.e. the newly started JVM crashes), restart it. Obviously you need to be able to recover, i.e. you need to keep track of what files to watch and you need to keep this data in your main process too.
Now the biggest remaining part is to implement some communication between the main process and the file watching process. This could be done using standard input/output of the file watching process or using a Socket/ServerSocket or some other mechanism.
I have a method that does some printing and I want the task to run on another thread (not on the EDT) because it might be creating a large file and I dont want the long process to freeze the GUI. The execution works perfectly on the EDT (with GUI freezing of course - which isn't desired), but when invoked on a different thread, it just doesn't execute. Here goes the method;
buildReceipt(itemList, method);
Where;
itemList is an ArrayList used to populate the receipt, and
method is an enum Type that determines whether to make the output a .pdf File or Send it directly to a Printer
The code above produces the document nicely when executed on the EDT but when I tried making it a background task using doInBackground() method of SwingWorker , it just didn't do anything at all; Then I got curious and tried the following;
Thread thread = new Thread(new Runnable(){
#Override
public void run()
{
buildReceipt(itemList, method);
}
});
thread.start();
and still, nothing happened......... More funny and confusing is the fact that I have even tried SwingUtilities.InvokeLater & SwingUtilities.InvokeAndWait (which by documentation run on the EDT) but still to no avail.
I have searched as many Stack Overflow related questions as I could, but none addresses my strange problem. I really need help on this one. Been stuck since yesteray?!?!?!
EDIT:
In Respose to Jean Waghetti; here's briefly what happens inside buildReceipt
private boolean buildReceipt(ArrayList<Sales> itemList, PrintMethod method)
{
boolean built = false;
if(!itemList.isEmpty())
{
InvoiceDesign design = new InvoiceDesign(itemList);
try
{
JasperReportBuilder report = design.build();
if(method.equals(PrintMethod.PDF))
{
appManager.connectToDB();
File fileDir = appManager.getReceiptsDir();
appManager.disconnectDB();
FileOutputStream fos = new FileOutputStream(fileDir);
report.toPdf(fos);
fos.close();
built = true;
}
else if(method.equals(PrintMethod.PRINTER))
{
report.print(true);
built = true;
}
}
catch(IOException e)
{
e.printStackTrace();
}
catch (DRException e)
{
e.printStackTrace();
}
}
return built;
}
So basically your item list is empty hence it never executes the code in the IF condition in that method.