JVM exits with window visible when main invoked by reflection - java

Mac OS X Catalina
JDK OpenJDK 14.0.2 (same issue 14.0.1)
public class PriceUpdateForm extends JPanel {
/* lots of code omitted */
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> startGUI());
/* try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
System.exit(17);
} */
}
private static void startGUI() {
final var form = new PriceUpdateForm();
form.j = new JFrame();
form.j.getContentPane().add(form);
form.j.pack();
form.j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
form.j.setVisible(true);
}
}
When this class's main method is invoked directly from the command line (or IntelliJ), it works as expected, even with the sleep commented out. The window stays visible until you click the close button, and then the program exits.
When, instead, I use a trampoline pattern where the class name is the first argument as shown below, the JVM is willing to exit even with the window visible. I had a hard time debugging this when the window disappeared right away, but if you uncomment the sleep, the JVM will exit after 3 seconds, with the window clearly visible until then.
Excerpt from main method of Trampoline
final Class<?> mainClass = Class.forName(args[0]);
final Method mainMethod = mainClass.getDeclaredMethod("main", newArgs.getClass());
mainMethod.invoke(null, new Object[]{newArgs});
newargs is just the original arguments minus the zeroth.
I get that without the sleep, the PriceUpdateForm.main is going to exit, but I don’t see why it takes the Window down with it. That’s been created on the GUI thread. It is not going out of scope, or, at least, its scope should be the same no matter how main was called: directly or by reflection.
Can someone explain this difference in behavior? I suppose I can make a years-long sleep, but that seems ugly.

Related

How to use a thread to run another JFrame while the main is still running

I have a jframe i want to display while my main frame is running. i want to pause my main code, until the user does the necessary actions on the other frame. I've read a lot of solutions but i need to see it done for my code to understand and grasp it fully. i do not want to use jdialog like I've seen listed as an answer before. My main goal is to understand better threading so that i can use what i learn in different cases.
With the code I've created, when running the thread, only just the frame loads, none of the other features are there on the frame. (the frame is simple it has a label, a list the user selects from, and a button to basically return the chosen list value.) its like the thread is cut off from completing or something.
here is my class calling the screen:
public class myThread implements Runnable {
String result = null;
public void run() {
MessageScreen ms = new MessageScreen();
ms.setVisible(true);
}
public String getResult() {
return result;
}
public void setResult(String AS) {
result = AS;
}
}
in my main code, a method is called that is returning a String[] value, with this method at some point i have the following code calling the new thread to get the value necessary to return in the original main method:
myThread mt = new myThread();
Thread t = new Thread(mt);
t.start();
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
myreturn = new String[] {"true", mt.getResult()};
without listing the whole code for the second frame, when the user presses the button, and at the end of the listener tied to the button press the i want to close the frame and return a string that was selected from the list:
jf.dispose();
myt.setResult(AdminSelection);
in the frame class, i have the following instance variables declared:
String AdminSelection = null;
myThread myt;
i hope this is enough information for someone to help me out and understand whats gone wrong here.
The function join() waits until the end of the run() method, when you do t.join(), your thread is already or almost ended. This is because in your run() method there is nothing that blocks the thread until the user has clicked the confirm button. And is better like this!
There is no sense to create a thread here, you should use a callback, or more generally in Java, a listener. You can take a look at Creating Custom Listeners.
But, especially if you want to pause your main code, you should use a (modal) JDialog which is made for this! Don't try to block the UI by yourself, you could block the UI thread (handled by Swing/AWT) by mistake. Creating a JDialog is better because everything is already made for this usage on the UI thread.
Also, you must know that create a Thread is really long, use a Thread when you really need it.

Force method to finish before continuing

I'm working on some sensitive LWJGL code and need to make sure that I create my display, and therefore GL context before executing any other code.
To give a clear example of my current predicament, take the following:
public static void main(String[] args) {
GLDisplay display = new GLDisplay();
display.start();
GLShader shader = new StaticShader();
}
The beginning of my GL creation happens in display.start(), where a separate thread is created, and within the separate thread, my Display is created.
Except this is where the problem lies, I have it in a separate thread. So then my program goes on and starts prematurely executing the new StaticShader() which calls even more GL code, breaking the program. (Can't execute before display is created).
What I'm trying to do, is achieve two threads simultaneously which I already have, but make sure that start() method is called completely before anything else is.
Here is how the start method works:
public synchronized void start() {
Threader.createThread(this, "GLDisplay");
}
#Override // public class GLDisplay extends Runnable
public void run() {
// GL code goes here.
}
And here is Threader:
public static void createThread(Runnable behaviour, String name) {
new Thread(behaviour, name + behaviour.hashCode()).start();
}
Now you may notice the synchronized keyword in the start method, well thats just one attempt I've had to no avail. I've also tried the following (which I actually grabbed from another StackOverflow answer):
#Override
public void run() {
synchronized(this) {
// GL code
}
}
I've checked other StackOverflow answers but either don't understand them or don't help me in my case. With the first code block I give in the main method, that is how I want my code to look to the person using it. I'm trying to put the thread-creation inside GlDisplay to hide it.
Any ideas?
Edit:
I can't simply wait for GLDisplay to close either (with Thread.join()) because there lies a while-loop that updates the display for the entirety of the program.
This is the entire reason I multi-threaded it. To allow this forever-ending loop to run while I do other things in the program. By closing the thread, I close the loop, cleanup the display and free the GL context from memory, once again making the shader code fail for lack of an existing context.
You can use java.util.concurrent.CountDownLatch to achieve it which aids in making a thread(s) wait till the operations on other threads is complete. Please see the reference on on what and how to use it.
Example:
public static void main(String[] args) {
CountDownLatch cdl = new CountDownLatch(1);
// pass the CountDownLatch into display
GLDisplay display = new GLDisplay(cdl);
display.start();
// wait for the latch to have been counted down in the disp thread
try
{
cdl.await();
}
catch (InterruptedException e)
{
e.printStackTrace();
}
GLShader shader = new StaticShader();
}
In your GLDisplay thread, call the countDown method of CountDownLatch
I might be misunderstanding something, but try the following:
public static void createThread(Runnable behaviour, String name) {
Thread t = new Thread(behaviour, name + behaviour.hashCode()).start();
t.join();
}
By calling join() the program should wait for the thread to complete.
Well I remember now that I can't have GL code against two separate threads anyway, but thats besides the point.
I don't actually need to use any thread-lock classes or anything, but rather can just do something as simple as this:
private Boolean threadLock = true;
public void start() {
Threader.createThread(this, "GLDisplay");
while (true) {
synchronized(threadLock) {
if (!threadLock) break;
}
}
}
#Runnable
public void run() {
// Do GL code.
synchronized(threadLock) { threadLock = false; }
// Do the rest of whatever I'm doing.
}
When the threadlock is reached in the second thread and is released, the first thread continues doing it's activity. It's that simple!

How can I implement the 'restart application' feature in Java [duplicate]

This question already has answers here:
How can I restart a Java application?
(13 answers)
Closed 9 years ago.
In many software, after we make any changes, the software has to be restarted for the changes to take effect, and sometimes, there is an option to restart the software automatically. How can I implement this in Java?
This is what I have tried:
int o = JOptionPane.showConfirmDialog(
frame,
"<html>The previously selected preferences have been changed.<br>Watch must restart for the changes to take effect.<br> Restart now?</html>",
"Restart now?", JOptionPane.YES_NO_OPTION);
if(o == JOptionPane.YES_OPTION) {
try {
Process p = new ProcessBuilder("java", "Watch").start();
} catch(IOException e) {
e.printStackTrace();
}
frame.dispose();
However, this doesn't seem to work. The application just terminates. What am I missing here? Thanks in advance!
This looks interesting: Make your application restart on its own
Basically, you create a script to run your app. In your app, if the user choses to restart, a restart file is created, then the app exits. Upon exiting, the startup script checks for the existence of a restart file. If exists, call the app again.
I think this is hard using just the facilities of the JVM alone.
I've never done this, but if you really want to terminate the whole JVM in which your current application is running and start a completely new instance of it, I would probably try something along these lines:
From your main application thread, start a shell script / batch file (e.g. using Runtime.getRuntime().exec("...")` that does the following steps:
Forks or uses some other system facility of starting the next step(s) in the background.
Maybe wait some time so you can be sure the old instance is dead. Or wait until some kind of PID file or similar thing is removed telling you that the old instance is gone.
Start a new JVM with your applications main class, probably giving it some command line argument or setting some system property to notify this new instance that it is in fact, an automatically restarted instance (so it can react to this e.g. by continuing where your originally left off).
In parallel to step 1 in your first main app instance, maybe wait a small amount of time (to make sure the background stuff is actually executed) and call System.exit(0); or some other method of shutting down.
Maybe there is a simpler way, it's just the first way that I could think of.
What about the next:
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
buildAndShowGui(args);
}
});
}
public static void buildAndShowGui(final String[] args) {
final JFrame frame = new JFrame("Window");
frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
frame.setSize(100, 400);
frame.setLayout(new FlowLayout());
JButton button = new JButton("Click!");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
int option = JOptionPane.showConfirmDialog(frame, "Restart?");
if (option == JOptionPane.YES_OPTION) {
frame.dispose();
restart(args);
}
}
});
frame.add(button);
frame.setVisible(true);
frame.toFront();
}
public static void restart(String[] args) {
main(args);
}

updating jlabel

I am having trouble updating a jlabel in a method. here is my code:
JLabel curStatus = new JLabel("");
JButton jbtnSubmit;
public static void main(String[] args) {
test gui = new test();
gui.startGUI();
// gui.setCurStatus("testing!"); << seems to work here,
//but when i call it from another class, it doesn't want to run.
}
// Set up the GUI end for the user
public void startGUI() {
// These are all essential GUI pieces
new JTextArea("");
final JFrame jfrm = new JFrame("my program");
jfrm.setLayout(new FlowLayout());
jfrm.setSize(300, 300);
jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jbtnSubmit = new JButton("Submit");
jfrm.add(jbtnSubmit);
jfrm.add(curStatus);
jfrm.setVisible(true);
}
public void setCurStatus(String inCurStatus) {
curStatus.setText(inCurStatus);
curStatus.setVisible(true);
}
what is happening, is that the label, curStatus is not appearing. for example, here is a call:
gui1.setCurStatus("Now running diagnostics... Please wait!");
Your problem appears to be one of misplaced references.
Here is how you create your GUI:
public static void main(String[] args) {
test gui = new test();
gui.startGUI();
// gui.setCurStatus("testing!"); << seems to work here,
// but when i call it from another class, it doesn't want to run.
}
You create your "test" object (which should be named "Test" by the way to conform to Java naming conventions) inside of your main method. Since it is declared inside of main, this variable has scope only inside of main and is visible no where else.
You then tell us that you are calling the method like so:
gui1.setCurStatus("Now running diagnostics... Please wait!");
The gui1 variable refers to a test class object but it likely refers to a different object than the test object that is being displayed since the original displayed test object is only refered to by a variable local to the main method.
To solve this, you must make sure to call setCurStatus on the currently displayed test object. How to do this depends on the rest of your code, something you've refused to show us despite our requests for you to do so.
Edit: Based on your latest bit of posted code (which still won't compile for me since it is missing a method, createTasksFile(), my assumptions are correct, you are calling setCurStatus(...) on a gui object that is not the displayed one:
public static String[] runDiagnostics() throws IOException {
gui gui1 = new gui(); // (A)
gui1.setCurStatus("Now running diagnostics... Please wait!");
On line (A) you create a new gui object and call setCurStatus on it, but it is not the GUI object that is being displayed but a completely different and unrelated object. It's only relation is that it is an object of the same class as the one being displayed but that's it. The solution is to get a reference to the displayed GUI and call this method on that object, and that object only.
Also, Robin's assumptions are correct, in that even if you fix this, you're going to be stuck with a Swing concurrency issue. The JLabel won't update because the Swing thread is trying to open a file:
public static String[] runDiagnostics() throws IOException {
gui gui1 = new gui();
gui1.setCurStatus("Now running diagnostics... Please wait!");
int i = 0;
int errorsI = 0;
File f = new File("tasks.txt");
String[] errors = { "", "", "", "", "" };
// try to create the file three times
do {
f.createNewFile();
i++;
} while (!f.exists() && i < 3);
So we're both right. The solution to this is to open your file on a background thread, a SwingWorker would work nicely here.
Edit 2
So to fix the reference problem, pass a reference of the gui into the runDiagnostics method using a gui parameter. Then call the setCurStatus method on this parameter. For example:
public static String[] runDiagnostics(gui gui1) throws IOException {
//!! gui gui1 = new gui(); // !! no longer needed
gui1.setCurStatus("Now running diagnostics... Please wait!");
You would have to pass the GUI in when calling the method:
//!! results = taskBckg.runDiagnostics();
results = taskBckg.runDiagnostics(gui);
Also, please edit all your code so that it follows Java naming conventions. All class names should begin with a capital letter. This makes it much easier for others to understand what your code is doing.
I will have a guess as well based on the message you are trying to display, since the question lacks some essential information. Based on the
"Now running diagnostics... Please wait!"
message, I will assume you are running diagnostics and trying to update the UI on the same thread. The code you posted contains no obvious mistakes which would explain why your call
gui1.setCurStatus("Now running diagnostics... Please wait!");
would not update the label contents.
What you have to do is all explained in the Swing concurrency tutorial. The main point is that you update the UI on the Event dispatch thread, and you never perform heavy calculations on that thread since that will block the UI, leading to a terrible user experience. Heavy calculations should be done on a worker thread, for example by using the SwingWorker class, and only the update of the UI (for example for reporting progress) should happen on the EDT.
With this information and links you should be able to find all relevant information. Also on this site you will find multiple examples on how to use SwingWorker to perform background calculations and updating the UI, like for example my answer on a previous question

Correctly handling a Reload and Restart from the AppletViewer

When my applet starts up the first time from a clean environment, things work the way I expect them to. I spawn two threads, one for generic processing, and one for graphics. I do all GUI manipulation calls from the event dispatching thread. Start/Stop is handled correctly from the appletviewer, but Restart/Reload is not. I have a Canvas called drawCanvas as the only Component in my Applet's content pane, and I use double buffering to draw to it.
I observe the problem here:
public void start() {
/* ... some stuff */
executeOnEDTAndWait(
new Thread() {
#Override
public void run() {
/* ... more stuff ... */
setupDrawCanvas();
if( drawCanvas.isDisplayable() ) {
drawCanvas.createBufferStrategy(2);
/* ... some more stuff */
} else {
/* This is where it runs into difficulties */
}
/* ... */
Where setupDrawCanvas is defined like this:
private void setupDrawCanvas() {
setVisible(false);
setIgnoreRepaint(true);
getContentPane().removeAll();
drawCanvas = new Canvas();
drawCanvas.setName("drawCanvas");
drawCanvas.setSize(
newDrawCanvasDimension.width,
newDrawCanvasDimension.height);
drawCanvas.setIgnoreRepaint(true);
getContentPane().add(drawCanvas);
getContentPane().setVisible(true);
drawCanvas.setVisible(true);
setVisible(true);
}
Also, here's the relevant code in destroy()
public void destroy() {
/* .. some stuff .. */
/* dispose of drawCanvas */
drawCanvas.setVisible(false);
if( drawCanvas.getBufferStrategy() != null ) {
drawCanvas.getBufferStrategy().dispose();
}
/* reset and disable the applet's GUI */
setVisible(false);
getContentPane().removeAll();
removeAll();
/* .. some more stuff */
The first time through, everything works fine. When I do a restart from the appletviewer, first stop() is called which causes all my threads to enter into wait states. Then destroy() is called which wakes all my threads up again and lets them exit, as well as do and invokeAndWait() on the EDT to clean up my widgets and do a setVisible(false). So after destroy completes the appletviewer calls init/start again and the process repeats exactly as before, except it fails in start() at the region I noted above.
Something that I noticed which made very little sense to me was that if I cloned the applet using the appletviewer and then reloaded the clone, everything would work as expected when I attempted to restart or reload the clone the first time, but would crash with an exception the second time.
Something else I noticed while trying to debug this problem is that the appletviewer and a browser act completely differently as hosts to my applet; they don't even call init() and start() under the same conditions. Also, restart and reload seem to be nothing more than a call to stop() -> destroy() -> init() -> start() but with subtle modifications to the execution environment.
So my question is, what is the significance of the restart and reload operations (i.e. when are they used) and is it a problem that my applet fails in the appletviewer when they occur?
Nice Question.
To answer this question we need understand the blocks of java code first.
we have a anonymous, static blocks before construtor that will be executed.
package com.test;
import java.applet.Applet;
import java.awt.*;
public class AppletTest extends Applet {
{
System.out.println("I m Anonymous block");
}
static {
System.out.println("I m static block");
}
public AppletTest()
{
System.out.println("I m constructor");
}
public void init()
{
System.out.println("init");
}
public void start()
{
System.out.println("start");
}
public void stop()
{
System.out.println("stop");
}
public void destroy()
{
System.out.println("destory");
}
public void paint(Graphics g)
{
g.drawString("test Applet",10,10);
}
}
invocation:
<applet code="AppletTest.class" height=300 width=300></applet>
when running this class using appletviewer you can note the difference.
Applet running for the first time you will get
I m static block
I m Anonymous block
I m constructor
init
start
while doing applet restart -
stop
destory
init
start
and on applet reload
stop
destory
I m Anonymous block
I m constructor
init
start
for your second question, applet does not guarantee the same output on different OS, network and hardware components.

Categories