]> source.dussan.org Git - vaadin-framework.git/commitdiff
Added more exception handling to PushHandler (#12578, #11882)
authorMarc Englund <marc@vaadin.com>
Wed, 9 Oct 2013 12:31:06 +0000 (15:31 +0300)
committerVaadin Code Review <review@vaadin.com>
Mon, 21 Oct 2013 08:16:37 +0000 (08:16 +0000)
PushHandler now catches Exception and calls ErrorHandler more.

Change-Id: I7032c00f717b1dae34f4352abc035b1b398c7cfc

server/src/com/vaadin/server/communication/PushHandler.java
uitest/src/com/vaadin/tests/push/PushErrorHandling.html [new file with mode: 0644]
uitest/src/com/vaadin/tests/push/PushErrorHandling.java [new file with mode: 0644]

index 81dd00084d1fef64ec7a133cb58961123959a3d5..b7cc628856c92d61cefa0a08147ee87a5077c854 100644 (file)
@@ -31,6 +31,8 @@ import org.atmosphere.cpr.AtmosphereResourceEvent;
 import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
 import org.json.JSONException;
 
+import com.vaadin.server.ErrorEvent;
+import com.vaadin.server.ErrorHandler;
 import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException;
 import com.vaadin.server.ServiceException;
 import com.vaadin.server.ServletPortletHelper;
@@ -274,14 +276,52 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
                 } else {
                     callback.run(resource, ui);
                 }
-            } catch (IOException e) {
-                getLogger().log(Level.WARNING, "Error writing a push response",
-                        e);
+            } catch (final IOException e) {
+                callErrorHandler(session, e);
+            } catch (final Exception e) {
+                SystemMessages msg = service.getSystemMessages(
+                        ServletPortletHelper.findLocale(null, null,
+                                vaadinRequest), vaadinRequest);
+                sendNotificationAndDisconnect(
+                        resource,
+                        VaadinService.createCriticalNotificationJSON(
+                                msg.getInternalErrorCaption(),
+                                msg.getInternalErrorMessage(), null,
+                                msg.getInternalErrorURL()));
+                callErrorHandler(session, e);
             } finally {
-                session.unlock();
+                try {
+                    session.unlock();
+                } catch (Exception e) {
+                    getLogger().log(Level.WARNING,
+                            "Error while unlocking session", e);
+                    // can't call ErrorHandler, we (hopefully) don't have a lock
+                }
             }
         } finally {
-            service.requestEnd(vaadinRequest, null, session);
+            try {
+                service.requestEnd(vaadinRequest, null, session);
+            } catch (Exception e) {
+                getLogger().log(Level.WARNING, "Error while ending request", e);
+
+                // can't call ErrorHandler, we don't have a lock
+            }
+        }
+    }
+
+    /**
+     * Call the session's {@link ErrorHandler}, if it has one, with the given
+     * exception wrapped in an {@link ErrorEvent}.
+     */
+    private void callErrorHandler(VaadinSession session, Exception e) {
+        try {
+            ErrorHandler errorHandler = ErrorEvent.findErrorHandler(session);
+            if (errorHandler != null) {
+                errorHandler.error(new ErrorEvent(e));
+            }
+        } catch (Exception ex) {
+            // Let's not allow error handling to cause trouble; log fails
+            getLogger().log(Level.WARNING, "ErrorHandler call failed", ex);
         }
     }
 
diff --git a/uitest/src/com/vaadin/tests/push/PushErrorHandling.html b/uitest/src/com/vaadin/tests/push/PushErrorHandling.html
new file mode 100644 (file)
index 0000000..afd3e70
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
+<link rel="selenium.base" href="http://localhost:8888/" />
+<title>PushErrorHandling</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">PushErrorHandling</td></tr>
+</thead><tbody>
+<tr>
+       <td>open</td>
+       <td>/run/PushErrorHandling?restartApplication</td>
+       <td></td>
+</tr>
+<tr>
+       <td>click</td>
+       <td>vaadin=runPushErrorHandling::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+       <td></td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runPushErrorHandling::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VLabel[0]</td>
+       <td>An error! Unable to invoke method click in com.vaadin.shared.ui.button.ButtonServerRpc</td>
+</tr>
+<tr>
+       <td>mouseClick</td>
+       <td>vaadin=runPushErrorHandling::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td>
+       <td>26,7</td>
+</tr>
+<tr>
+       <td>assertText</td>
+       <td>vaadin=runPushErrorHandling::Root/VNotification[0]/HTML[0]/domChild[0]</td>
+       <td>Internal error</td>
+</tr>
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/push/PushErrorHandling.java b/uitest/src/com/vaadin/tests/push/PushErrorHandling.java
new file mode 100644 (file)
index 0000000..3074bd3
--- /dev/null
@@ -0,0 +1,93 @@
+package com.vaadin.tests.push;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import com.vaadin.data.util.AbstractInMemoryContainer;
+import com.vaadin.data.util.BeanContainer;
+import com.vaadin.event.ItemClickEvent;
+import com.vaadin.event.ItemClickEvent.ItemClickListener;
+import com.vaadin.server.ErrorHandler;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.shared.communication.PushMode;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Label;
+import com.vaadin.ui.Table;
+
+public class PushErrorHandling extends AbstractTestUI {
+
+    @Override
+    protected void setup(VaadinRequest request) {
+        getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
+
+        VaadinSession.getCurrent().setErrorHandler(new ErrorHandler() {
+
+            @Override
+            public void error(com.vaadin.server.ErrorEvent event) {
+                addComponent(new Label("An error! "
+                        + event.getThrowable().getMessage()));
+                System.err.println("An error! "
+                        + event.getThrowable().getMessage());
+            }
+        });
+
+        final Button button = new Button("Click for NPE!",
+                new Button.ClickListener() {
+                    @Override
+                    public void buttonClick(ClickEvent event) {
+                        ((String) null).length(); // Null-pointer exception
+                    }
+                });
+        addComponent(button);
+
+        final Table view = new Table("testtable");
+        view.setSelectable(true);
+        view.setMultiSelect(false);
+        view.setImmediate(true);
+        view.setSizeFull();
+
+        view.addItemClickListener(new ItemClickListener() {
+
+            @Override
+            public void itemClick(ItemClickEvent event) {
+                BeanContainer<String, AbstractInMemoryContainer> metaContainer = new BeanContainer<String, AbstractInMemoryContainer>(
+                        AbstractInMemoryContainer.class) {
+                    @Override
+                    public Collection<String> getContainerPropertyIds() {
+                        List<String> cpropIds = new ArrayList<String>(super
+                                .getContainerPropertyIds());
+                        cpropIds.add("testid");
+                        return cpropIds;
+                    }
+
+                    @Override
+                    public Class<?> getType(Object propertyId) {
+                        ((Object) null).hashCode();
+                        return super.getType(propertyId);
+                    }
+                };
+                view.setContainerDataSource(metaContainer);
+
+            }
+        });
+        view.addContainerProperty("Column", String.class, "Click for NPE");
+        view.addItem(new Object());
+
+        addComponent(view);
+
+    }
+
+    @Override
+    protected String getTestDescription() {
+        return "Error handling should still work w/ push enabled. (Button can be handled properly, table causes internal error)";
+    }
+
+    @Override
+    protected Integer getTicketNumber() {
+        return 11882;
+    }
+}