In my JavaFX application, I want to show an error dialog and exit the app whenever some unexpected exception occurs. So in my main-method I have set up a default uncaught exception handler before launching the app:
setDefaultUncaughtExceptionHandler((thread, cause) -> {
try {
cause.printStackTrace();
final Runnable showDialog = () -> {
// create dialog and show
};
if (Platform.isFxApplicationThread()) {
showDialog.run();
} else {
runAndWait(showDialog);
}
} catch (Throwable t) {
// ???
} finally {
System.exit(-1);
}
});
launch(MyApp.class);
Explanation: When the uncaught exception handler is executed on the JavaFX Application Thread (FXAT), I just run the code for showing the dialog. This of course doesn't work when the exception handler is not invoked by the FXAT. In this case, I have to push the code onto the FXAT. But I can't use Platform.runLater because then my app would exit before the dialog is shown. So, I made that custom method runAndWait which internally pushes the runnable via Platform.runLater, but waits until the execution of the runnable (with some countdown latch mechanism).
Now the problem with this: When an exception occurs in my start() method then my app gets stuck. Because it tries to wait until the execution of the dialog showing, but the FXAT never does this execution. I guess this is because when the start() method fails with an exception, the FXAT is just dead? I'm not sure whether this is a special case for the start() method or whether this will happen in any situation when an exception is thrown and not caught within code that is executed by the FXAT.
In Swing as I know the EDT is a complex architecture consisting of several threads. It wasn't the case that when some execution on the EDT failed that the entire Swing broke down. But here this is what seems to happen?
So what can I do here? How can I show to the user that the application cannot start?
Well....
I have a solution but I don't particularly recommend it. By default, Application.launch() will catch the exception thrown by the start method, exit the FX Platform, and then rethrow the exception. Since the FX Application Thread has shut down when your default uncaught exception handler executes, waiting for something to happen on the FX Application Thread just blocks indefinitely.
The exception to this is when the FX Application is running in web start. The way that the launcher checks for this is to check for the presence of a security manager. So a (really, really ugly) workaround is to install a security manager so that it looks like you are running in web start mode. This line will install a permissive security manager:
System.setSecurityManager(new SecurityManager(){
#Override
public void checkPermission(Permission perm) {}
});
Here's a SSCCE:
import java.lang.Thread.UncaughtExceptionHandler;
import java.security.Permission;
import java.util.concurrent.FutureTask;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
public class ShowDialogOnException {
public static final UncaughtExceptionHandler ALERT_EXCEPTION_HANDLER = (thread, cause) -> {
try {
cause.printStackTrace();
final Runnable showDialog = () -> {
Alert alert = new Alert(AlertType.ERROR);
alert.setContentText("An unknown error occurred");
alert.showAndWait();
};
if (Platform.isFxApplicationThread()) {
showDialog.run();
} else {
FutureTask<Void> showDialogTask = new FutureTask<Void>(showDialog, null);
Platform.runLater(showDialogTask);
showDialogTask.get();
}
} catch (Throwable t) {
t.printStackTrace();
} finally {
System.exit(-1);
}
};
public static void main(String[] args) {
System.setSecurityManager(new SecurityManager(){
#Override
public void checkPermission(Permission perm) {}
});
Thread.setDefaultUncaughtExceptionHandler(ALERT_EXCEPTION_HANDLER);
Application.launch(App.class, args);
}
}
and a test app:
import javafx.application.Application;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
throw new Exception("An exception");
}
#Override
public void stop() {
System.out.println("Stop");
}
}
As I said, this is really something of a big hack, and I don't really recommend this unless you have no other option.
Related
One operator deleted Data Dictionary and restarted Alfresco 3.4.12 Enterprise Edition. The context /alfresco doesn't start with the following exception:
17:43:11,100 INFO [STDOUT] 17:43:11,097 ERROR [web.context.ContextLoader] Context initialization failed
org.alfresco.error.AlfrescoRuntimeException: 08050000 Failed to find 'app:dictionary' node
at org.alfresco.repo.action.scheduled.ScheduledPersistedActionServiceImpl.locatePersistanceFolder(ScheduledPersistedActionServiceImpl.java:132)
Looking at the source code in org.alfresco.repo.action.scheduled.ScheduledPersistedActionServiceImpl.java, the path is hardwired.
Then we followed the tip from https://community.alfresco.com/thread/202859-error-failed-to-find-appdictionary-node, editing bootstrap-context.xml, comment out the class.
After the change the error went over, now the RenditionService couldn't start.
We're looking for a way to recover the deleted node, since we can obtain the nodeid from the database. So we created a small class and invoke it through spring in bootstrap-context.xml, but it's failing due to permissions. Could you take a look at the code and tell us what's wrong. The code is:
package com.impulseit.test;
import javax.transaction.UserTransaction;
import org.alfresco.repo.node.archive.NodeArchiveService;
import org.alfresco.repo.node.archive.RestoreNodeReport;
import org.alfresco.repo.security.authentication.AuthenticationUtil;
import org.alfresco.repo.security.authentication.AuthenticationUtil.RunAsWork;
import org.alfresco.service.ServiceRegistry;
import org.alfresco.service.cmr.repository.NodeRef;
public class RestoreNode {
private NodeArchiveService nodeArchiveService;
private ServiceRegistry serviceRegistry;
private String nodeName ="archive://SpacesStore/adfc0cfe-e20b-467f-ad71-253aea8f9ac9";
public void setNodeArchiveService(NodeArchiveService value)
{
this.nodeArchiveService = value;
}
public void setServiceRegistry(ServiceRegistry value)
{
this.serviceRegistry = value;
}
public void doRestore() {
RunAsWork<Void> runAsWork = new RunAsWork<Void>()
{
public Void doWork() throws Exception
{
NodeRef nodeRef = new NodeRef(nodeName);
//RestoreNodeReport restoreNodeReport =
UserTransaction trx_A = serviceRegistry.getTransactionService().getUserTransaction();
trx_A.begin();
AuthenticationUtil.setFullyAuthenticatedUser(AuthenticationUtil.getSystemUserName());
RestoreNodeReport restored = nodeArchiveService.restoreArchivedNode(nodeRef);
trx_A.commit();
return null;
}
};
AuthenticationUtil.runAs(runAsWork,AuthenticationUtil.getSystemUserName());
}
public RestoreNode() {
}
}
The exception is:
19:31:21,747 User:admin ERROR [node.archive.NodeArchiveServiceImpl] An unhandled exception stopped the restore
java.lang.NullPointerException
at org.alfresco.repo.security.permissions.impl.model.PermissionModel.getPermissionReference(PermissionModel.java:1315)
at org.alfresco.repo.security.permissions.impl.PermissionServiceImpl.getPermissionReference(PermissionServiceImpl.java:956)
at org.alfresco.repo.security.permissions.impl.PermissionServiceImpl.hasPermission(PermissionServiceImpl.java:976)
Thank you in advance.
Luis
I'm trying to get a responsive JavaFX graphical interface while executing a cmd command.
The command I'm executing is the following.
youtube-dl.exe --audio-format mp3 --extract-audio https://www.youtube.com/watch?v=l2vy6pJSo9c
As you see this is a youtube-downloader that converts a youtube link to an mp3-file.
I want this to be executed in a second thread and not in the main FX thread.
I've solved this by implementing interface Callable in the class StartDownloadingThread.
#Override
public Process call() throws Exception {
Process p = null;
p = ExecuteCommand(localCPara1, localCPara2, localDirectory).start();
try {
Thread.sleep(30);
}catch (InterruptedException e){}
return p;
}
The method ExecuteCommand just returns a ProcessBuilder object.
I try to use Thread.sleep to make the program return to the main thread and thus making the application responsive. Unfortunately the program still freezes.
This is how the method call is called.
ExecutorService pool = Executors.newFixedThreadPool(2);
StartDownloadingThread callable = new StartDownloadingThread(parameter1, parameter2, directory);
Future future = pool.submit(callable);
Process p = (Process) future.get();
p.waitFor();
How do I make my GUI responsive using the interface Callable?
Using a executor to run a task just for you to use the get method of the Future that is returned when submitting the task does not actually free the original thread to continue with other tasks. Later you even use the waitFor method on the original thread, which is likely to take even more time than anything you do in your Callable.
For this purpose the Task class may be better suited, since it allows you to handle success/failure on the application thread using event handlers.
Also please make sure an ExecutorService is shut down after you're done submitting tasks.
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
Process p = null;
p = ExecuteCommand(localCPara1, localCPara2, localDirectory).start();
// why are you even doing this?
try {
Thread.sleep(30);
}catch (InterruptedException e){}
// do the rest of the long running things
p.waitFor();
return null;
}
};
task.setOnSucceeded(event -> {
// modify ui to show success
});
task.setOnFailed(event -> {
// modify ui to show failure
});
ExecutorService pool = Executors.newFixedThreadPool(2);
pool.submit(task);
// add more tasks...
// shutdown the pool not keep the jvm alive because of the pool
pool.shutdown();
I'm trying to open a web url in the default system browser from javafx. I didn't find any official documentation regard this. Any clue?
EDIT:
I've found a tutorial but it doesn't work.
I'm using MacOsX and I tried launching
java.awt.Desktop.getDesktop().browse(new URI(url));
but I get an HeadlessExcelption
Use hostServices.showDocument(location).
Try placing the following code in your application's start method:
getHostServices().showDocument("http://www.yahoo.com");
Complementing jewelsea's answer, if you don't know how to call getHostServices() then try this at your main class:
HostServicesDelegate hostServices = HostServicesFactory.getInstance(this);
hostServices.showDocument(WEBSITE);
http://docs.oracle.com/javafx/2/api/javafx/application/HostServices.html#showDocument(java.lang.String)
Another option is to use ProcessBuilder:
public static void openWebpage(String url) {
try {
new ProcessBuilder("x-www-browser", url).start();
} catch (IOException e) {
e.printStackTrace();
}
}
You can use this option if Desktop.getDesktop().browse(uri) for some reason hangs without any error.
Try This:
try {
Desktop.getDesktop().browse(new URL("https://google.com").toURI());
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
It cannot be done, seems, because this feature is not implemented : https://javafx-jira.kenai.com/browse/RT-210
The matter is that you will not be able to launch anything, what requires awt-stack and jfx in the same VM. The decision - is to use a separate JVM. Just launch a separate VM, and accept commands on browsing by socket.
That is one way, another way - is to find any other way of browser call from java - this is a task not specific to javafx-2, but to java at all.
But developer has added a comment :
Anthony Petrov added a comment - May, 17 2013 05:09 PM
Note that FX8 allows headful AWT to run in the same VM with FX. So the AWT API should work.
Here is a script that works inside the scene controller, when a button is activated:
package sample;
import com.sun.deploy.uitoolkit.impl.fx.HostServicesFactory;
import com.sun.javafx.application.HostServicesDelegate;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.stage.Stage;
public class Controller extends Application {
public void openBrowser(ActionEvent actionEvent) throws Exception {
HostServicesDelegate hostServices = HostServicesFactory.getInstance(this);
getHostServices().showDocument("http://www.yahoo.com");
}
#Override
public void start(Stage primaryStage) throws Exception {
}
}
I am trying to run the following program.I am using glassfish server 3.1.2 to enable this MDB to run.Then too I am unanble to run it.
package com.mdb;
import javax.jms.ConnectionFactory;
import javax.jms.Queue;
import javax.jms.Connection;
import javax.jms.Session;
import javax.jms.QueueBrowser;
import javax.jms.Message;
import javax.jms.JMSException;
import javax.annotation.Resource;
import java.util.Enumeration;
import javax.ejb.Stateless;
/**
* The MessageBrowser class inspects a queue and displays the messages it
* holds.
*/
#Stateless
public class MessageClient {
#Resource(mappedName = "jms/ConnectionFactory")
private static ConnectionFactory connectionFactory;
#Resource(mappedName = "jms/Queue")
private static Queue queue;
/**
* Main method.
*
* #param args the queue used by the example
*/
public static void main(String[] args) {
Connection connection = null;
try {
System.out.println("1");
connection = connectionFactory.createConnection();
System.out.println("2");
Session session = connection.createSession(
false,
Session.AUTO_ACKNOWLEDGE);
QueueBrowser browser = session.createBrowser(queue);
Enumeration msgs = browser.getEnumeration();
if (!msgs.hasMoreElements()) {
System.out.println("No messages in queue");
} else {
while (msgs.hasMoreElements()) {
Message tempMsg = (Message) msgs.nextElement();
System.out.println("Message: " + tempMsg);
}
}
} catch (JMSException e) {
System.err.println("Exception occurred: " + e.toString());
} finally {
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
}
}
}
}
}
The problem is I get the follwing exsception upon runing it.
Exception in thread "main" java.lang.NullPointerException
at com.mdb.MessageClient.main(MessageClient.java:35)
What may be the problem here?
What you have build is not a MDB. It's a stateless session bean that browses a queue.
A MDB has the #MessageDriven annotation. It's invoked whenever a message comes in.
Apart from that, you might want to use the "lookup" attribute instead of the "mappedName" one. The latter is from an ancient time when people weren't sure yet about anything, and needed a temporary hack to make things magically work.
Your usage of static fields and the static main method inside a stateless bean make no sense at all. If you're accessing your bean via that main method you're not using the bean at all and you're just calling an isolated global-like method. If anything, this might be the source of your NPE.
The fix isn't really simple. You're seemingly completely confused between Java EE and Java SE, and between instances and static methods.
I had been using jmock with seam all these days, but its not sufficient to mock final/static/enums. So I tried working with JMockit. However everytime I run, I get NPE. Can't even debug, below is sample code
public class TestJmockit extends SeamTest {
#Mocked Dependency dependencyInCodeToTest;
CodeToTest bean = new CodeToTest();
#Test
public void testSaveSectionChangesJMockit() throws Exception {
new AbstractSeamTest.ComponentTest() {
#Override
protected void testComponents() throws Exception {
new NonStrictExpectations()
{
{
dependencyInCodeToTest.getLabel(); result = "Normal";
}
};
bean.execute();
}
}.run();
}
}
Actual Code..
package com.abc.action.account.information;
import com.abc.vo.account.ExternalAccountStatus;
import com.abc.vo.account.information.ExternalAccountStatusClosedInfo;
import com.abc.vo.account.information.ExternalAccountStatusInfo;
import mockit.Mocked;
import mockit.NonStrictExpectations;
import org.jboss.seam.mock.AbstractSeamTest;
import org.jboss.seam.mock.SeamTest;
import org.junit.Test;
public class ConsumerAccountInformationActionTestJmockit extends SeamTest {
#Mocked ExternalAccountStatus mockExternalAccountStatus;
#Mocked ExternalAccountStatusInfo mockExternalAccountStatusInfo;
// ConsumerAccountInformationAction bean = new ConsumerAccountInformationAction();
#Test
public void testSaveSectionChangesJMockit() throws Exception {
new AbstractSeamTest.ComponentTest() {
#Override
protected void testComponents() throws Exception {
new NonStrictExpectations()
{
{
mockExternalAccountStatus.getLabel(); result = "Normal";
mockExternalAccountStatusInfo.getClosedInfo(); result = new ExternalAccountStatusClosedInfo();
}
};
// bean.saveSectionChanges();
}
}.run();
}
}
If I put a breakpoint at class declaratiom (Public Class Consumer..), stepping over to next line causes NPE. If I take out the commented lines in the code, it fails at the first uncommented line.
I am using Java 1.6 and IntelliJ IDE. Wonder if it has to do with IDE configuration.
With TestNG I dont even get the stack trace, with JUnit I see the below..
java.lang.NullPointerException
at org.jboss.seam.servlet.ServletApplicationMap.get(ServletApplicationMap.java:54)
at org.jboss.seam.contexts.BasicContext.get(BasicContext.java:49)
at org.jboss.seam.contexts.BasicContext.get(BasicContext.java:44)
at org.jboss.seam.core.Init.instance(Init.java:117)
at org.jboss.seam.contexts.BusinessProcessContext.<init>(BusinessProcessContext.java:47)
at org.jboss.seam.contexts.TestLifecycle.beginTest(TestLifecycle.java:35)
at org.jboss.seam.mock.AbstractSeamTest$ComponentTest.run(AbstractSeamTest.java:159)
at com.billmelater.csa.action.account.information.ConsumerAccountInformationActionTestJmockit.testSaveSectionChangesJMockit(ConsumerAccountInformationActionTestJmockit.java:27)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:44)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:41)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:73)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:46)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:180)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:41)
at org.junit.runners.ParentRunner$1.evaluate(ParentRunner.java:173)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:28)
at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:31)
at org.junit.runners.ParentRunner.run(ParentRunner.java:220)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:71)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:199)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:62)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
Process finished with exit code 255
General Objection: mocking is there to isolate you from external code,
especially from those coming from your server wendor ( like AbstractSeamtest ) - so
you do not have to initialize them or have server runing or whatever.
Treat mocked test as saved debug session. In your case you like to assure (I'm guessing), that method
bean.saveSectionChanges();
interacts correctly with seam infrastructure, or other collaborators. This is easily achieved by something like:
public static testProperInteraction(#Mocked final Collaborator collaborator) {
new Expectations() {
{
collaborator.doThis(with some parameters);
returns(something you like);
}
};
Bean bean = new Bean(collaborator);
assertSomething(bean.saveSessionChanges());
// nothing else shall be called
new FullVerifications() {
{
}
};
}