From c454797201969a03fa43000d8bb8ccb30ef5b437 Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Mon, 19 Nov 2012 13:40:14 +0200 Subject: [PATCH] Replaced ComponentErrorHandler with generic ErrorHandler (#10231) * Replaced AbstractComponent ErrorHandler with ClientConnector level error handler. Now uses the same ErrorHandler as other parts of the framework. * Made error handling hierarchical so that the error handler of the connector where the error occured or its ancestors is used. Falls back to VaadinSession ErrorHandler. * Changed ErrorEvent to be a class as all other events in the framework * Renamed ErrorListener to ErrorHandler and DefaultErrorListener to DefaultErrorHandler for consistency * Unified error handling in AbstractCommunicationManager * Unified error handling in VaadinServlet and VaadinPortlet * Removed superfluous ErrorEvent implementations (#10232) Change-Id: Ied518e05209fe54685f4bebab0709b1cd5584fd1 --- .../server/AbstractClientConnector.java | 21 +++ .../server/AbstractCommunicationManager.java | 105 +++----------- .../server/ChangeVariablesErrorEvent.java | 51 ------- .../com/vaadin/server/ClientConnector.java | 47 ++++++ .../vaadin/server/DefaultErrorHandler.java | 102 +++++++++++++ .../vaadin/server/DefaultErrorListener.java | 62 -------- .../com/vaadin/server/DragAndDropService.java | 12 ++ server/src/com/vaadin/server/ErrorEvent.java | 100 ++++++++++++- .../{ErrorListener.java => ErrorHandler.java} | 8 +- .../src/com/vaadin/server/FileResource.java | 14 +- .../com/vaadin/server/LegacyApplication.java | 6 +- .../src/com/vaadin/server/StreamVariable.java | 2 +- .../src/com/vaadin/server/VaadinPortlet.java | 32 ++--- .../src/com/vaadin/server/VaadinServlet.java | 31 ++-- .../src/com/vaadin/server/VaadinSession.java | 27 +--- .../src/com/vaadin/server/VariableOwner.java | 13 -- .../src/com/vaadin/ui/AbstractComponent.java | 61 +------- server/src/com/vaadin/ui/Component.java | 14 -- .../vaadin/tests/appengine/GAESyncTest.java | 4 +- .../TerminalErrorNotification.java | 2 +- .../components/AbstractComponentTest.java | 2 +- .../abstractfield/TextFieldConversions.java | 9 +- .../button/ShortCutListenerModification.java | 4 +- .../tests/errorhandler/ErrorHandlers.java | 134 ++++++++++++++++++ 24 files changed, 478 insertions(+), 385 deletions(-) delete mode 100644 server/src/com/vaadin/server/ChangeVariablesErrorEvent.java create mode 100644 server/src/com/vaadin/server/DefaultErrorHandler.java delete mode 100644 server/src/com/vaadin/server/DefaultErrorListener.java rename server/src/com/vaadin/server/{ErrorListener.java => ErrorHandler.java} (78%) create mode 100644 uitest/src/com/vaadin/tests/errorhandler/ErrorHandlers.java diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index ecdab22160..45c6da7e77 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -90,6 +90,8 @@ public abstract class AbstractClientConnector implements ClientConnector, */ private EventRouter eventRouter = null; + private ErrorHandler errorHandler = null; + /** * @deprecated As of 7.0.0, use {@link #markAsDirty()} instead */ @@ -951,4 +953,23 @@ public abstract class AbstractClientConnector implements ClientConnector, } + /* + * (non-Javadoc) + * + * @see com.vaadin.server.ClientConnector#getErrorHandler() + */ + public ErrorHandler getErrorHandler() { + return errorHandler; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.server.ClientConnector#setErrorHandler(com.vaadin.server. + * ErrorHandler) + */ + public void setErrorHandler(ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + } diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java index d1180c479b..86a4f67b4c 100644 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java @@ -63,6 +63,7 @@ import org.json.JSONObject; import com.vaadin.annotations.JavaScript; import com.vaadin.annotations.PreserveOnRefresh; import com.vaadin.annotations.StyleSheet; +import com.vaadin.server.ClientConnector.ConnectorErrorEvent; import com.vaadin.server.ComponentSizeValidator.InvalidLayout; import com.vaadin.server.RpcManager.RpcInvocationException; import com.vaadin.server.StreamVariable.StreamingEndEvent; @@ -77,7 +78,6 @@ import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.communication.SharedState; import com.vaadin.shared.communication.UidlValue; import com.vaadin.shared.ui.ui.UIConstants; -import com.vaadin.ui.AbstractField; import com.vaadin.ui.Component; import com.vaadin.ui.ConnectorTracker; import com.vaadin.ui.HasComponents; @@ -299,8 +299,7 @@ public abstract class AbstractCommunicationManager implements Serializable { } catch (Exception e) { session.lock(); try { - handleChangeVariablesError(session, (Component) owner, e, - new HashMap()); + handleConnectorRelatedException(owner, e); } finally { session.unlock(); } @@ -347,8 +346,7 @@ public abstract class AbstractCommunicationManager implements Serializable { } catch (Exception e) { session.lock(); try { - handleChangeVariablesError(session, (Component) owner, e, - new HashMap()); + handleConnectorRelatedException(owner, e); } finally { session.unlock(); } @@ -1702,13 +1700,7 @@ public abstract class AbstractCommunicationManager implements Serializable { ServerRpcManager.applyInvocation(connector, (ServerRpcMethodInvocation) invocation); } catch (RpcInvocationException e) { - Throwable realException = e.getCause(); - Component errorComponent = null; - if (connector instanceof Component) { - errorComponent = (Component) connector; - } - handleChangeVariablesError(uI.getSession(), - errorComponent, realException, null); + handleConnectorRelatedException(connector, e); } } else { @@ -1730,17 +1722,7 @@ public abstract class AbstractCommunicationManager implements Serializable { + changes.keySet()); } } catch (Exception e) { - Component errorComponent = null; - if (connector instanceof Component) { - errorComponent = (Component) connector; - } else if (connector instanceof DragAndDropService) { - Object dropHandlerOwner = changes.get("dhowner"); - if (dropHandlerOwner instanceof Component) { - errorComponent = (Component) dropHandlerOwner; - } - } - handleChangeVariablesError(uI.getSession(), - errorComponent, e, changes); + handleConnectorRelatedException(connector, e); } } } @@ -1755,6 +1737,24 @@ public abstract class AbstractCommunicationManager implements Serializable { return success; } + /** + * Handles an exception that occurred when processing Rpc calls or a file + * upload. + * + * @param ui + * The UI where the exception occured + * @param throwable + * The exception + * @param connector + * The Rpc target + */ + private void handleConnectorRelatedException(ClientConnector connector, + Throwable throwable) { + ErrorEvent errorEvent = new ConnectorErrorEvent(connector, throwable); + ErrorHandler handler = ErrorEvent.findErrorHandler(connector); + handler.error(errorEvent); + } + /** * Parse a message burst from the client into a list of MethodInvocation * instances. @@ -1952,65 +1952,6 @@ public abstract class AbstractCommunicationManager implements Serializable { return result; } - public class ErrorHandlerErrorEvent implements ErrorEvent, Serializable { - private final Throwable throwable; - - public ErrorHandlerErrorEvent(Throwable throwable) { - this.throwable = throwable; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - - } - - /** - * Handles an error (exception) that occurred when processing variable - * changes from the client or a failure of a file upload. - * - * For {@link AbstractField} components, - * {@link AbstractField#handleError(com.vaadin.ui.AbstractComponent.ComponentErrorEvent)} - * is called. In all other cases (or if the field does not handle the - * error), {@link ErrorListener#terminalError(ErrorEvent)} for the session - * error handler is called. - * - * @param session - * @param owner - * component that the error concerns - * @param e - * exception that occurred - * @param m - * map from variable names to values - */ - private void handleChangeVariablesError(VaadinSession session, - Component owner, Throwable t, Map m) { - boolean handled = false; - ChangeVariablesErrorEvent errorEvent = new ChangeVariablesErrorEvent( - owner, t, m); - - if (owner instanceof AbstractField) { - try { - handled = ((AbstractField) owner).handleError(errorEvent); - } catch (Exception handlerException) { - /* - * If there is an error in the component error handler we pass - * the that error to the session error handler and continue - * processing the actual error - */ - session.getErrorHandler().terminalError( - new ErrorHandlerErrorEvent(handlerException)); - handled = false; - } - } - - if (!handled) { - session.getErrorHandler().terminalError(errorEvent); - } - - } - /** * Unescape encoded burst separator characters in a burst received from the * client. This protects from separator injection attacks. diff --git a/server/src/com/vaadin/server/ChangeVariablesErrorEvent.java b/server/src/com/vaadin/server/ChangeVariablesErrorEvent.java deleted file mode 100644 index f208fffcef..0000000000 --- a/server/src/com/vaadin/server/ChangeVariablesErrorEvent.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2011 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server; - -import java.util.Map; - -import com.vaadin.ui.AbstractComponent.ComponentErrorEvent; -import com.vaadin.ui.Component; - -@SuppressWarnings("serial") -public class ChangeVariablesErrorEvent implements ComponentErrorEvent { - - private Throwable throwable; - private Component component; - - private Map variableChanges; - - public ChangeVariablesErrorEvent(Component component, Throwable throwable, - Map variableChanges) { - this.component = component; - this.throwable = throwable; - this.variableChanges = variableChanges; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - - public Component getComponent() { - return component; - } - - public Map getVariableChanges() { - return variableChanges; - } - -} \ No newline at end of file diff --git a/server/src/com/vaadin/server/ClientConnector.java b/server/src/com/vaadin/server/ClientConnector.java index b46ef58fcd..440f282508 100644 --- a/server/src/com/vaadin/server/ClientConnector.java +++ b/server/src/com/vaadin/server/ClientConnector.java @@ -37,6 +37,32 @@ import com.vaadin.ui.UI; * */ public interface ClientConnector extends Connector { + /** + * An error event for connector related errors. Use {@link #getConnector()} + * to find the connector where the error occurred or {@link #getComponent()} + * to find the nearest parent component. + */ + public static class ConnectorErrorEvent extends + com.vaadin.server.ErrorEvent { + + private Connector connector; + + public ConnectorErrorEvent(Connector connector, Throwable t) { + super(t); + this.connector = connector; + } + + /** + * Gets the connector for which this error occurred. + * + * @return The connector for which the error occurred + */ + public Connector getConnector() { + return connector; + } + + } + /** * Returns the list of pending server to client RPC calls and clears the * list. @@ -249,4 +275,25 @@ public interface ClientConnector extends Connector { * @return RpcManager or null if none found for the interface */ public RpcManager getRpcManager(String rpcInterfaceName); + + /** + * Gets the error handler for the connector. + * + * The error handler is dispatched whenever there is an error processing the + * data coming from the client to this connector. + * + * @return The error handler or null if not set + */ + public ErrorHandler getErrorHandler(); + + /** + * Sets the error handler for the connector. + * + * The error handler is dispatched whenever there is an error processing the + * data coming from the client for this connector. + * + * @param errorHandler + * The error handler for this connector + */ + public void setErrorHandler(ErrorHandler errorHandler); } diff --git a/server/src/com/vaadin/server/DefaultErrorHandler.java b/server/src/com/vaadin/server/DefaultErrorHandler.java new file mode 100644 index 0000000000..de77b48a25 --- /dev/null +++ b/server/src/com/vaadin/server/DefaultErrorHandler.java @@ -0,0 +1,102 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server; + +import java.net.SocketException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.server.ClientConnector.ConnectorErrorEvent; +import com.vaadin.shared.Connector; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Component; + +public class DefaultErrorHandler implements ErrorHandler { + @Override + public void error(ErrorEvent event) { + doDefault(event); + } + + public static void doDefault(ErrorEvent event) { + final Throwable t = event.getThrowable(); + if (t instanceof SocketException) { + // Most likely client browser closed socket + getLogger().info( + "SocketException in CommunicationManager." + + " Most likely client (browser) closed socket."); + return; + } + + // Finds the original source of the error/exception + AbstractComponent component = findAbstractComponent(event); + if (component != null) { + // Shows the error in AbstractComponent + ErrorMessage errorMessage = AbstractErrorMessage + .getErrorMessageForException(t); + component.setComponentError(errorMessage); + } + + // also print the error on console + getLogger().log(Level.SEVERE, "", t); + } + + private static Logger getLogger() { + return Logger.getLogger(DefaultErrorHandler.class.getName()); + } + + /** + * Returns the AbstractComponent associated with the given error if such can + * be found + * + * @param event + * The error to investigate + * @return The {@link AbstractComponent} to error relates to or null if + * could not be determined or if the error does not relate to any + * AbstractComponent. + */ + public static AbstractComponent findAbstractComponent( + com.vaadin.server.ErrorEvent event) { + if (event instanceof ConnectorErrorEvent) { + Component c = findComponent(((ConnectorErrorEvent) event) + .getConnector()); + if (c instanceof AbstractComponent) { + return (AbstractComponent) c; + } + } + + return null; + } + + /** + * Finds the nearest component by traversing upwards in the hierarchy. If + * connector is a Component, that Component is returned. Otherwise, looks + * upwards in the hierarchy until it finds a {@link Component}. + * + * @return A Component or null if no component was found + */ + public static Component findComponent(Connector connector) { + if (connector instanceof Component) { + return (Component) connector; + } + if (connector.getParent() != null) { + return findComponent(connector.getParent()); + } + + return null; + } + +} \ No newline at end of file diff --git a/server/src/com/vaadin/server/DefaultErrorListener.java b/server/src/com/vaadin/server/DefaultErrorListener.java deleted file mode 100644 index 71fccf8d0c..0000000000 --- a/server/src/com/vaadin/server/DefaultErrorListener.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2011 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server; - -import java.net.SocketException; -import java.util.logging.Level; -import java.util.logging.Logger; - -import com.vaadin.ui.AbstractComponent; - -public class DefaultErrorListener implements ErrorListener { - @Override - public void terminalError(ErrorEvent event) { - doDefault(event); - } - - public static void doDefault(ErrorEvent event) { - final Throwable t = event.getThrowable(); - if (t instanceof SocketException) { - // Most likely client browser closed socket - getLogger().info( - "SocketException in CommunicationManager." - + " Most likely client (browser) closed socket."); - return; - } - - // Finds the original source of the error/exception - Object owner = null; - if (event instanceof VariableOwner.ErrorEvent) { - owner = ((VariableOwner.ErrorEvent) event).getVariableOwner(); - } else if (event instanceof ChangeVariablesErrorEvent) { - owner = ((ChangeVariablesErrorEvent) event).getComponent(); - } - - // Shows the error in AbstractComponent - if (owner instanceof AbstractComponent) { - ((AbstractComponent) owner).setComponentError(AbstractErrorMessage - .getErrorMessageForException(t)); - } - - // also print the error on console - getLogger().log(Level.SEVERE, "Terminal error:", t); - } - - private static Logger getLogger() { - return Logger.getLogger(DefaultErrorListener.class.getName()); - } -} \ No newline at end of file diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java index a864f8fb16..0fa39fa0e8 100644 --- a/server/src/com/vaadin/server/DragAndDropService.java +++ b/server/src/com/vaadin/server/DragAndDropService.java @@ -53,6 +53,8 @@ public class DragAndDropService implements VariableOwner, ClientConnector { private AcceptCriterion acceptCriterion; + private ErrorHandler errorHandler; + public DragAndDropService(AbstractCommunicationManager manager) { this.manager = manager; } @@ -346,4 +348,14 @@ public class DragAndDropService implements VariableOwner, ClientConnector { VaadinResponse response, String path) throws IOException { return false; } + + @Override + public ErrorHandler getErrorHandler() { + return errorHandler; + } + + @Override + public void setErrorHandler(ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } } diff --git a/server/src/com/vaadin/server/ErrorEvent.java b/server/src/com/vaadin/server/ErrorEvent.java index b570271cf7..27754bfae6 100644 --- a/server/src/com/vaadin/server/ErrorEvent.java +++ b/server/src/com/vaadin/server/ErrorEvent.java @@ -17,14 +17,108 @@ package com.vaadin.server; import java.io.Serializable; +import com.vaadin.shared.Connector; +import com.vaadin.ui.UI; + /** - * An error event implementation for Terminal. + * An error thrown by the framework and handled by an {@link ErrorHandler}. + * Typically handled by {@link VaadinSession#getErrorHandler()} but can also be + * handled by a {@link Connector} specific handler, set using + * {@link ClientConnector#setErrorHandler(ErrorHandler)}. + * */ -public interface ErrorEvent extends Serializable { +public class ErrorEvent implements Serializable { + + private Throwable throwable; + + public ErrorEvent(Throwable t) { + setThrowable(t); + } /** * Gets the contained throwable, the cause of the error. + * + * @return + */ + public Throwable getThrowable() { + return throwable; + } + + public void setThrowable(Throwable throwable) { + this.throwable = throwable; + } + + /** + * Method for finding the error handler for the given connector. Uses + * connector hierarchy to find a connector with an error handler. Falls back + * to the VaadinSession error handler if no connector has specified an error + * handler. + *

+ * Returns a {@link DefaultErrorHandler} if no error handler was found + *

+ * + * @param connector + * The target connector + * @return An ErrorHandler for the connector + */ + public static ErrorHandler findErrorHandler(ClientConnector connector) { + if (connector != null) { + ErrorHandler errorHandler = connector.getErrorHandler(); + if (errorHandler != null) { + return errorHandler; + } + + ClientConnector parent = connector.getParent(); + if (parent != null) { + return findErrorHandler(parent); + } + + /* + * Reached UI and found no error handler. Try session which + * typically has one. + */ + UI ui = connector.getUI(); + if (ui != null) { + errorHandler = findErrorHandler(ui.getSession()); + if (errorHandler != null) { + return errorHandler; + } + } + } + + /* + * No connector known or the connector is not attached to a session. Try + * the current session + */ + if (VaadinSession.getCurrent() != null) { + ErrorHandler errorHandler = VaadinSession.getCurrent() + .getErrorHandler(); + if (errorHandler != null) { + return errorHandler; + } + } + + /* + * We should never really get here as at least the session should have + * an error handler. If for some reason it does not we use the default + * error handler. + */ + return new DefaultErrorHandler(); + } + + /** + * Method for finding the error handler for the given session. + * + * @param connector + * The target connector + * + * @return An ErrorHandler for the session or null if none was found */ - public Throwable getThrowable(); + public static ErrorHandler findErrorHandler(VaadinSession session) { + if (session == null) { + return null; + } + return session.getErrorHandler(); + } } \ No newline at end of file diff --git a/server/src/com/vaadin/server/ErrorListener.java b/server/src/com/vaadin/server/ErrorHandler.java similarity index 78% rename from server/src/com/vaadin/server/ErrorListener.java rename to server/src/com/vaadin/server/ErrorHandler.java index 07400852e0..36500a7f53 100644 --- a/server/src/com/vaadin/server/ErrorListener.java +++ b/server/src/com/vaadin/server/ErrorHandler.java @@ -18,15 +18,15 @@ package com.vaadin.server; import java.io.Serializable; /** - * Interface for listening to Terminal errors. + * Interface for listening to errors in the application. */ -public interface ErrorListener extends Serializable { +public interface ErrorHandler extends Serializable { /** - * Invoked when a terminal error occurs. + * Invoked when an error occurs. * * @param event * the fired event. */ - public void terminalError(ErrorEvent event); + public void error(ErrorEvent event); } \ No newline at end of file diff --git a/server/src/com/vaadin/server/FileResource.java b/server/src/com/vaadin/server/FileResource.java index c413bb471d..42f214c64a 100644 --- a/server/src/com/vaadin/server/FileResource.java +++ b/server/src/com/vaadin/server/FileResource.java @@ -71,18 +71,8 @@ public class FileResource implements ConnectorResource { ds.setCacheTime(cacheTime); return ds; } catch (final FileNotFoundException e) { - // Log the exception using the application error handler - VaadinSession.getCurrent().getErrorHandler() - .terminalError(new ErrorEvent() { - - @Override - public Throwable getThrowable() { - return e; - } - - }); - - return null; + throw new RuntimeException("File not found: " + + sourceFile.getName(), e); } } diff --git a/server/src/com/vaadin/server/LegacyApplication.java b/server/src/com/vaadin/server/LegacyApplication.java index dc55bca344..598cfbf832 100644 --- a/server/src/com/vaadin/server/LegacyApplication.java +++ b/server/src/com/vaadin/server/LegacyApplication.java @@ -38,7 +38,7 @@ import com.vaadin.ui.UI; * @since 7.0 */ @Deprecated -public abstract class LegacyApplication implements ErrorListener { +public abstract class LegacyApplication implements ErrorHandler { private LegacyWindow mainWindow; private String theme; @@ -208,8 +208,8 @@ public abstract class LegacyApplication implements ErrorListener { } @Override - public void terminalError(ErrorEvent event) { - DefaultErrorListener.doDefault(event); + public void error(ErrorEvent event) { + DefaultErrorHandler.doDefault(event); } public VaadinSession getContext() { diff --git a/server/src/com/vaadin/server/StreamVariable.java b/server/src/com/vaadin/server/StreamVariable.java index a75cc2f0d7..8a664f22c0 100644 --- a/server/src/com/vaadin/server/StreamVariable.java +++ b/server/src/com/vaadin/server/StreamVariable.java @@ -151,7 +151,7 @@ public interface StreamVariable extends Serializable { * the streaming ended before the end of the input. The streaming may fail * due an interruption by {@link } or due an other unknown exception in * communication. In the latter case the exception is also passed to - * {@link VaadinSession#terminalError(com.vaadin.server.Terminal.ErrorEvent)} + * {@link VaadinSession#error(com.vaadin.server.Terminal.ErrorEvent)} * . */ public interface StreamingErrorEvent extends StreamingEvent { diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java index d7d4a65245..05b6ac360a 100644 --- a/server/src/com/vaadin/server/VaadinPortlet.java +++ b/server/src/com/vaadin/server/VaadinPortlet.java @@ -21,7 +21,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.Serializable; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.security.GeneralSecurityException; @@ -653,6 +652,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants { // portlet // if this was an UIDL request, response UIDL back to client + ErrorHandler errorHandler = ErrorEvent.findErrorHandler(vaadinSession); if (getRequestType(request) == RequestType.UIDL) { SystemMessages ci = getService().getSystemMessages( ServletPortletHelper.findLocale(null, vaadinSession, @@ -660,33 +660,17 @@ public class VaadinPortlet extends GenericPortlet implements Constants { criticalNotification(request, response, ci.getInternalErrorCaption(), ci.getInternalErrorMessage(), null, ci.getInternalErrorURL()); - if (vaadinSession != null) { - vaadinSession.getErrorHandler().terminalError( - new RequestError(e)); + if (errorHandler != null) { + errorHandler.error(new ErrorEvent(e)); + } + } else { + if (errorHandler != null) { + errorHandler.error(new ErrorEvent(e)); } else { + // Re-throw other exceptions throw new PortletException(e); } - } else { - // Re-throw other exceptions - throw new PortletException(e); } - - } - - @SuppressWarnings("serial") - public class RequestError implements ErrorEvent, Serializable { - - private final Throwable throwable; - - public RequestError(Throwable throwable) { - this.throwable = throwable; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - } /** diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index e48153642b..3089eccc12 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -21,7 +21,6 @@ import java.io.InputStream; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; -import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; @@ -573,6 +572,8 @@ public class VaadinServlet extends HttpServlet implements Constants { private void handleServiceException(VaadinServletRequest request, VaadinServletResponse response, VaadinSession vaadinSession, Throwable e) throws IOException, ServletException { + ErrorHandler errorHandler = ErrorEvent.findErrorHandler(vaadinSession); + // if this was an UIDL request, response UIDL back to client if (getRequestType(request) == RequestType.UIDL) { SystemMessages ci = getService().getSystemMessages( @@ -581,15 +582,16 @@ public class VaadinServlet extends HttpServlet implements Constants { criticalNotification(request, response, ci.getInternalErrorCaption(), ci.getInternalErrorMessage(), null, ci.getInternalErrorURL()); - if (vaadinSession != null) { - vaadinSession.getErrorHandler().terminalError( - new RequestError(e)); + if (errorHandler != null) { + errorHandler.error(new ErrorEvent(e)); + } + } else { + if (errorHandler != null) { + errorHandler.error(new ErrorEvent(e)); } else { + // Re-throw other exceptions throw new ServletException(e); } - } else { - // Re-throw other exceptions - throw new ServletException(e); } } @@ -1217,21 +1219,6 @@ public class VaadinServlet extends HttpServlet implements Constants { return u; } - public class RequestError implements ErrorEvent, Serializable { - - private final Throwable throwable; - - public RequestError(Throwable throwable) { - this.throwable = throwable; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - - } - /** * Escapes characters to html entities. An exception is made for some * "safe characters" to keep the text somewhat readable. diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java index 9bfd03b402..87f5be3e2c 100644 --- a/server/src/com/vaadin/server/VaadinSession.java +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -90,7 +90,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * Session wide error handler which is used by default if an error is left * unhandled. */ - private ErrorListener errorHandler = new DefaultErrorListener(); + private ErrorHandler errorHandler = new DefaultErrorHandler(); /** * The converter factory that is used to provide default converters for the @@ -338,7 +338,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * * @return the current error handler */ - public ErrorListener getErrorHandler() { + public ErrorHandler getErrorHandler() { return errorHandler; } @@ -347,7 +347,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * * @param errorHandler */ - public void setErrorHandler(ErrorListener errorHandler) { + public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } @@ -388,27 +388,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { this.converterFactory = converterFactory; } - /** - * Application error is an error message defined on the application level. - * - * When an error occurs on the application level, this error message4 type - * should be used. This indicates that the problem is caused by the - * application - not by the user. - */ - public class ApplicationError implements ErrorEvent { - private final Throwable throwable; - - public ApplicationError(Throwable throwable) { - this.throwable = throwable; - } - - @Override - public Throwable getThrowable() { - return throwable; - } - - } - /** * Adds a request handler to this session. Request handlers can be added to * provide responses to requests that are not handled by the default diff --git a/server/src/com/vaadin/server/VariableOwner.java b/server/src/com/vaadin/server/VariableOwner.java index e4319cfaf3..825a5804ef 100644 --- a/server/src/com/vaadin/server/VariableOwner.java +++ b/server/src/com/vaadin/server/VariableOwner.java @@ -81,17 +81,4 @@ public interface VariableOwner extends Serializable { */ public boolean isImmediate(); - /** - * VariableOwner error event. - */ - public interface ErrorEvent extends com.vaadin.server.ErrorEvent { - - /** - * Gets the source VariableOwner. - * - * @return the variable owner. - */ - public VariableOwner getVariableOwner(); - - } } diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 2ce3e25b2a..811a1dc2f6 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -30,6 +30,7 @@ import com.vaadin.event.ShortcutListener; import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.ClientConnector; import com.vaadin.server.ComponentSizeValidator; +import com.vaadin.server.ErrorHandler; import com.vaadin.server.ErrorMessage; import com.vaadin.server.Resource; import com.vaadin.server.VaadinSession; @@ -83,7 +84,7 @@ public abstract class AbstractComponent extends AbstractClientConnector private static final Pattern sizePattern = Pattern .compile("^(-?\\d+(\\.\\d+)?)(%|px|em|ex|in|cm|mm|pt|pc)?$"); - private ComponentErrorHandler errorHandler = null; + private ErrorHandler errorHandler = null; /** * Keeps track of the Actions added to this component; the actual @@ -891,64 +892,6 @@ public abstract class AbstractComponent extends AbstractClientConnector } } - public interface ComponentErrorEvent extends com.vaadin.server.ErrorEvent { - } - - public interface ComponentErrorHandler extends Serializable { - /** - * Handle the component error - * - * @param event - * @return True if the error has been handled False, otherwise - */ - public boolean handleComponentError(ComponentErrorEvent event); - } - - /** - * Gets the error handler for the component. - * - * The error handler is dispatched whenever there is an error processing the - * data coming from the client. - * - * @return - */ - public ComponentErrorHandler getErrorHandler() { - return errorHandler; - } - - /** - * Sets the error handler for the component. - * - * The error handler is dispatched whenever there is an error processing the - * data coming from the client. - * - * If the error handler is not set, the application error handler is used to - * handle the exception. - * - * @param errorHandler - * AbstractField specific error handler - */ - public void setErrorHandler(ComponentErrorHandler errorHandler) { - this.errorHandler = errorHandler; - } - - /** - * Handle the component error event. - * - * @param error - * Error event to handle - * @return True if the error has been handled False, otherwise. If the error - * haven't been handled by this component, it will be handled in the - * application error handler. - */ - public boolean handleError(ComponentErrorEvent error) { - if (errorHandler != null) { - return errorHandler.handleComponentError(error); - } - return false; - - } - /* * Actions */ diff --git a/server/src/com/vaadin/ui/Component.java b/server/src/com/vaadin/ui/Component.java index 4797bb702c..85140a8351 100644 --- a/server/src/com/vaadin/ui/Component.java +++ b/server/src/com/vaadin/ui/Component.java @@ -953,20 +953,6 @@ public interface Component extends ClientConnector, Sizeable, Serializable { } } - /** - * Listener interface for receiving Component.Errorss. - */ - public interface ErrorListener extends EventListener, Serializable { - - /** - * Notifies the listener of a component error. - * - * @param event - * the event that has occured. - */ - public void componentError(Component.ErrorEvent event); - } - /** * A sub-interface implemented by components that can obtain input focus. * This includes all {@link Field} components as well as some other diff --git a/uitest/src/com/vaadin/tests/appengine/GAESyncTest.java b/uitest/src/com/vaadin/tests/appengine/GAESyncTest.java index f5d90bae79..146e85f5f3 100644 --- a/uitest/src/com/vaadin/tests/appengine/GAESyncTest.java +++ b/uitest/src/com/vaadin/tests/appengine/GAESyncTest.java @@ -29,7 +29,7 @@ public class GAESyncTest extends LegacyApplication { } @Override - public void terminalError(com.vaadin.server.ErrorEvent event) { + public void error(com.vaadin.server.ErrorEvent event) { Throwable t = event.getThrowable(); // Was this caused by a GAE timeout? while (t != null) { @@ -41,7 +41,7 @@ public class GAESyncTest extends LegacyApplication { t = t.getCause(); } - super.terminalError(event); + super.error(event); } diff --git a/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.java b/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.java index 9dee2fe0b6..2c8d7e01af 100644 --- a/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.java +++ b/uitest/src/com/vaadin/tests/application/TerminalErrorNotification.java @@ -38,7 +38,7 @@ public class TerminalErrorNotification extends TestBase { } @Override - public void terminalError(com.vaadin.server.ErrorEvent event) { + public void error(com.vaadin.server.ErrorEvent event) { event.getThrowable().printStackTrace(); UI mainWindow = getMainWindow(); diff --git a/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java b/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java index f36437326a..c086e03ae0 100644 --- a/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java +++ b/uitest/src/com/vaadin/tests/components/AbstractComponentTest.java @@ -715,7 +715,7 @@ public abstract class AbstractComponentTest } @Override - public void terminalError(com.vaadin.server.ErrorEvent event) { + public void error(com.vaadin.server.ErrorEvent event) { String logMsg = "Exception occured, " + event.getThrowable().getClass().getName(); diff --git a/uitest/src/com/vaadin/tests/components/abstractfield/TextFieldConversions.java b/uitest/src/com/vaadin/tests/components/abstractfield/TextFieldConversions.java index 3a8275bd51..888922b69d 100644 --- a/uitest/src/com/vaadin/tests/components/abstractfield/TextFieldConversions.java +++ b/uitest/src/com/vaadin/tests/components/abstractfield/TextFieldConversions.java @@ -5,9 +5,9 @@ import java.util.Date; import com.vaadin.data.Property.ValueChangeEvent; import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.data.util.ObjectProperty; +import com.vaadin.server.ErrorEvent; +import com.vaadin.server.ErrorHandler; import com.vaadin.server.UserError; -import com.vaadin.ui.AbstractComponent.ComponentErrorEvent; -import com.vaadin.ui.AbstractComponent.ComponentErrorHandler; import com.vaadin.ui.ComboBox; import com.vaadin.ui.TextField; @@ -41,12 +41,11 @@ public class TextFieldConversions extends AbstractComponentDataBindingTest { tf = new TextField("TextField"); addComponent(tf); - tf.setErrorHandler(new ComponentErrorHandler() { + tf.setErrorHandler(new ErrorHandler() { @Override - public boolean handleComponentError(ComponentErrorEvent event) { + public void error(ErrorEvent event) { tf.setComponentError(new UserError("Invalid value")); - return true; } }); } diff --git a/uitest/src/com/vaadin/tests/components/button/ShortCutListenerModification.java b/uitest/src/com/vaadin/tests/components/button/ShortCutListenerModification.java index 51ca47b4b7..9cdd803fe0 100644 --- a/uitest/src/com/vaadin/tests/components/button/ShortCutListenerModification.java +++ b/uitest/src/com/vaadin/tests/components/button/ShortCutListenerModification.java @@ -57,8 +57,8 @@ public class ShortCutListenerModification extends TestBase implements } @Override - public void terminalError(com.vaadin.server.ErrorEvent event) { - super.terminalError(event); + public void error(com.vaadin.server.ErrorEvent event) { + super.error(event); getMainWindow().showNotification("Failed!", Notification.TYPE_ERROR_MESSAGE); diff --git a/uitest/src/com/vaadin/tests/errorhandler/ErrorHandlers.java b/uitest/src/com/vaadin/tests/errorhandler/ErrorHandlers.java new file mode 100644 index 0000000000..c5ff1be1ed --- /dev/null +++ b/uitest/src/com/vaadin/tests/errorhandler/ErrorHandlers.java @@ -0,0 +1,134 @@ +package com.vaadin.tests.errorhandler; + +import java.lang.reflect.InvocationTargetException; + +import com.vaadin.event.ListenerMethod.MethodException; +import com.vaadin.server.DefaultErrorHandler; +import com.vaadin.server.ErrorHandler; +import com.vaadin.server.RpcManager.RpcInvocationException; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Component; +import com.vaadin.ui.Label; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Notification.Type; +import com.vaadin.ui.VerticalLayout; + +public class ErrorHandlers extends AbstractTestUI { + + public static class NotificationErrorHandler implements ErrorHandler { + + @Override + public void error(com.vaadin.server.ErrorEvent event) { + Notification.show(getErrorMessage(event), Type.ERROR_MESSAGE); + } + + } + + @Override + protected void setup(VaadinRequest request) { + addComponent(runtimeExceptionOnClick(new Button("Standard button"))); + addComponent(npeOnClick(new Button("Standard button with NPE"))); + Button customErrorButton = notificationErrorHandler(new Button( + "Button with notification error handler")); + addComponent(runtimeExceptionOnClick(customErrorButton)); + + final VerticalLayout layoutWithErrorHandler = new VerticalLayout( + runtimeExceptionOnClick(new Button("Error handler on parent"))); + ErrorHandler e = new ErrorHandler() { + + @Override + public void error(com.vaadin.server.ErrorEvent event) { + layoutWithErrorHandler.addComponent(new Label("Layout error: " + + getErrorMessage(event))); + } + + }; + layoutWithErrorHandler.setErrorHandler(e); + layoutWithErrorHandler + .addComponent(notificationErrorHandler(npeOnClick(new Button( + "Error handler on button and parent")))); + addComponent(layoutWithErrorHandler); + } + + private Button notificationErrorHandler(Button button) { + button.setErrorHandler(new NotificationErrorHandler()); + return button; + } + + protected static String getErrorMessage(com.vaadin.server.ErrorEvent event) { + Component c = DefaultErrorHandler.findAbstractComponent(event); + String errorMsg = "Error: '" + getMessage(event) + "' in "; + errorMsg += c.getClass().getSimpleName() + " with caption '" + + c.getCaption() + "'"; + return errorMsg; + } + + private static String getMessage(com.vaadin.server.ErrorEvent event) { + Throwable e = getUserCodeException(event); + if (e.getMessage() != null) { + return e.getMessage(); + } else { + return e.getClass().getSimpleName(); + } + } + + private static Throwable getUserCodeException( + com.vaadin.server.ErrorEvent event) { + Throwable t = event.getThrowable(); + if (t instanceof RpcInvocationException) { + t = t.getCause(); + } + if (t instanceof InvocationTargetException) { + t = t.getCause(); + } + if (t instanceof MethodException) { + t = t.getCause(); + } + + return t; + + } + + private Button runtimeExceptionOnClick(Button customErrorButton) { + customErrorButton.setCaption("RE: " + customErrorButton.getCaption()); + + customErrorButton.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + throw new RuntimeException("Fail in click event"); + } + }); + return customErrorButton; + } + + private Button npeOnClick(Button customErrorButton) { + customErrorButton.setCaption("NPE: " + customErrorButton.getCaption()); + customErrorButton.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + Integer i = null; + i += 2; + } + }); + return customErrorButton; + } + + @Override + protected String getTestDescription() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Integer getTicketNumber() { + // TODO Auto-generated method stub + return null; + } + +} -- 2.39.5