From 92af8c26fc058869ba902e65adab799fb3370a7b Mon Sep 17 00:00:00 2001 From: Artur Signell Date: Fri, 22 Aug 2008 07:25:23 +0000 Subject: [PATCH] Fixes for #2002 and #1642 - improved exception handling in CommunicationManager svn changeset:5242/svn branch:trunk --- src/com/itmill/toolkit/Application.java | 45 +- .../gwt/server/ChangeVariablesErrorEvent.java | 34 ++ .../gwt/server/CommunicationManager.java | 532 +++++++++--------- .../toolkit/tests/tickets/Ticket2002.java | 50 ++ src/com/itmill/toolkit/ui/AbstractField.java | 63 +++ 5 files changed, 469 insertions(+), 255 deletions(-) create mode 100644 src/com/itmill/toolkit/terminal/gwt/server/ChangeVariablesErrorEvent.java create mode 100644 src/com/itmill/toolkit/tests/tickets/Ticket2002.java diff --git a/src/com/itmill/toolkit/Application.java b/src/com/itmill/toolkit/Application.java index 2c072284ff..1196891014 100644 --- a/src/com/itmill/toolkit/Application.java +++ b/src/com/itmill/toolkit/Application.java @@ -5,6 +5,7 @@ package com.itmill.toolkit; import java.net.MalformedURLException; +import java.net.SocketException; import java.net.URL; import java.util.Collection; import java.util.Collections; @@ -27,6 +28,7 @@ import com.itmill.toolkit.terminal.SystemError; import com.itmill.toolkit.terminal.Terminal; import com.itmill.toolkit.terminal.URIHandler; import com.itmill.toolkit.terminal.VariableOwner; +import com.itmill.toolkit.terminal.gwt.server.ChangeVariablesErrorEvent; import com.itmill.toolkit.ui.AbstractComponent; import com.itmill.toolkit.ui.Component; import com.itmill.toolkit.ui.Window; @@ -177,6 +179,12 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener private Focusable pendingFocus; + /** + * Application wide error handler which is used by default if an error is + * left unhandled. + */ + private Terminal.ErrorListener errorHandler = this; + /** *

* Gets a window by name. Returns null if the application is @@ -1074,8 +1082,17 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener * @see com.itmill.toolkit.terminal.Terminal.ErrorListener#terminalError(com.itmill.toolkit.terminal.Terminal.ErrorEvent) */ public void terminalError(Terminal.ErrorEvent event) { - // throw it to standard error stream too - event.getThrowable().printStackTrace(); + Throwable t = event.getThrowable(); + if (t instanceof SocketException) { + // Most likely client browser closed socket + System.err + .println("Warning: SocketException in CommunicationManager." + + " Most likely client (browser) closed socket."); + + } else { + // throw it to standard error stream too + t.printStackTrace(); + } // Finds the original source of the error/exception Object owner = null; @@ -1085,6 +1102,8 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener owner = ((URIHandler.ErrorEvent) event).getURIHandler(); } else if (event instanceof ParameterHandler.ErrorEvent) { owner = ((ParameterHandler.ErrorEvent) event).getParameterHandler(); + } else if (event instanceof ChangeVariablesErrorEvent) { + owner = ((ChangeVariablesErrorEvent) event).getComponent(); } // Shows the error in AbstractComponent @@ -1145,6 +1164,28 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener return "NONVERSIONED"; } + /** + * Gets the application error handler. + * + * The default error handler is the application itself. + * + * @return Application error handler + */ + public Terminal.ErrorListener getErrorHandler() { + return errorHandler; + } + + /** + * Sets the application error handler. + * + * The default error handler is the application itself. + * + * @param errorHandler + */ + public void setErrorHandler(Terminal.ErrorListener errorHandler) { + this.errorHandler = errorHandler; + } + /** * Experimental API, not finalized. Contains the system messages used to * notify the user about various critical situations that can occur. diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ChangeVariablesErrorEvent.java b/src/com/itmill/toolkit/terminal/gwt/server/ChangeVariablesErrorEvent.java new file mode 100644 index 0000000000..08182b7155 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/ChangeVariablesErrorEvent.java @@ -0,0 +1,34 @@ +package com.itmill.toolkit.terminal.gwt.server; + +import java.util.Map; + +import com.itmill.toolkit.ui.Component; +import com.itmill.toolkit.ui.AbstractField.ComponentErrorEvent; + +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; + } + + public Throwable getThrowable() { + return throwable; + } + + public Component getComponent() { + return component; + } + + public Map getVariableChanges() { + return variableChanges; + } + +} \ No newline at end of file diff --git a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java index 86bbb39d2a..3b6c7882f4 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java @@ -12,7 +12,6 @@ import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.lang.reflect.Method; -import java.net.SocketException; import java.text.DateFormatSymbols; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -48,6 +47,8 @@ import com.itmill.toolkit.terminal.URIHandler; import com.itmill.toolkit.terminal.UploadStream; import com.itmill.toolkit.terminal.VariableOwner; import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; +import com.itmill.toolkit.terminal.Terminal.ErrorEvent; +import com.itmill.toolkit.ui.AbstractField; import com.itmill.toolkit.ui.Component; import com.itmill.toolkit.ui.Upload; import com.itmill.toolkit.ui.Window; @@ -192,10 +193,11 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { * @param request * @param response * @throws IOException + * @throws ServletException */ public void handleUidlRequest(HttpServletRequest request, HttpServletResponse response, ApplicationServlet applicationServlet) - throws IOException { + throws IOException, ServletException { // repaint requested or session has timed out and new one is created boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null) @@ -205,296 +207,274 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { final PrintWriter outWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out, "UTF-8"))); - try { - - // The rest of the process is synchronized with the application - // in order to guarantee that no parallel variable handling is - // made - synchronized (application) { - - // Finds the window within the application - Window window = null; - if (application.isRunning()) { - window = getApplicationWindow(request, application); - // Returns if no window found - if (window == null) { - // This should not happen, no windows exists but - // application is still open. - System.err - .println("Warning, could not get window for application with request URI " - + request.getRequestURI()); - return; - } - } else { - // application has been closed - endApplication(request, response, application); + // The rest of the process is synchronized with the application + // in order to guarantee that no parallel variable handling is + // made + synchronized (application) { + + // Finds the window within the application + Window window = null; + if (application.isRunning()) { + window = getApplicationWindow(request, application); + // Returns if no window found + if (window == null) { + // This should not happen, no windows exists but + // application is still open. + System.err + .println("Warning, could not get window for application with request URI " + + request.getRequestURI()); return; } + } else { + // application has been closed + endApplication(request, response, application); + return; + } - // Change all variables based on request parameters - if (!handleVariables(request, application)) { - // var inconsistency; the client is probably out-of-sync - SystemMessages ci = null; - try { - Method m = application.getClass().getMethod( - "getSystemMessages", null); - ci = (Application.SystemMessages) m.invoke(null, null); - } catch (Exception e2) { - // Not critical, but something is still wrong; print - // stacktrace - e2.printStackTrace(); - } - if (ci != null) { - String msg = ci.getOutOfSyncMessage(); - String cap = ci.getOutOfSyncCaption(); - if (msg != null || cap != null) { - applicationServlet.criticalNotification(request, - response, cap, msg, ci.getOutOfSyncURL()); - // will reload page after this - return; - } + // Change all variables based on request parameters + if (!handleVariables(request, application)) { + // var inconsistency; the client is probably out-of-sync + SystemMessages ci = null; + try { + Method m = application.getClass().getMethod( + "getSystemMessages", null); + ci = (Application.SystemMessages) m.invoke(null, null); + } catch (Exception e2) { + // Not critical, but something is still wrong; print + // stacktrace + e2.printStackTrace(); + } + if (ci != null) { + String msg = ci.getOutOfSyncMessage(); + String cap = ci.getOutOfSyncCaption(); + if (msg != null || cap != null) { + applicationServlet.criticalNotification(request, + response, cap, msg, ci.getOutOfSyncURL()); + // will reload page after this + return; } - // No message to show, let's just repaint all. - System.err - .println("Warning: variable inconsistency - client is probably out-of-sync, repainting all."); - repaintAll = true; - } + // No message to show, let's just repaint all. + System.err + .println("Warning: variable inconsistency - client is probably out-of-sync, repainting all."); + repaintAll = true; - // If repaint is requested, clean all ids in this root window - if (repaintAll) { - for (final Iterator it = idPaintableMap.keySet().iterator(); it - .hasNext();) { - final Component c = (Component) idPaintableMap.get(it - .next()); - if (isChildOf(window, c)) { - it.remove(); - paintableIdMap.remove(c); - } + } + + // If repaint is requested, clean all ids in this root window + if (repaintAll) { + for (final Iterator it = idPaintableMap.keySet().iterator(); it + .hasNext();) { + final Component c = (Component) idPaintableMap.get(it + .next()); + if (isChildOf(window, c)) { + it.remove(); + paintableIdMap.remove(c); } } + } - // Removes application if it has stopped during variable changes - if (!application.isRunning()) { - endApplication(request, response, application); - return; - } + // Removes application if it has stopped during variable changes + if (!application.isRunning()) { + endApplication(request, response, application); + return; + } - // Sets the response type - response.setContentType("application/json; charset=UTF-8"); - // some dirt to prevent cross site scripting - outWriter.print("for(;;);[{"); + // Sets the response type + response.setContentType("application/json; charset=UTF-8"); + // some dirt to prevent cross site scripting + outWriter.print("for(;;);[{"); - outWriter.print("\"changes\":["); + outWriter.print("\"changes\":["); - // re-get mainwindow - may have been changed - Window newWindow = getApplicationWindow(request, application); - if (newWindow != window) { - window = newWindow; - repaintAll = true; - } + // re-get mainwindow - may have been changed + Window newWindow = getApplicationWindow(request, application); + if (newWindow != window) { + window = newWindow; + repaintAll = true; + } - JsonPaintTarget paintTarget = new JsonPaintTarget(this, - outWriter, !repaintAll); + JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter, + !repaintAll); - // Paints components - ArrayList paintables; - if (repaintAll) { - paintables = new ArrayList(); - paintables.add(window); + // Paints components + ArrayList paintables; + if (repaintAll) { + paintables = new ArrayList(); + paintables.add(window); - // Reset sent locales - locales = null; - requireLocale(application.getLocale().toString()); + // Reset sent locales + locales = null; + requireLocale(application.getLocale().toString()); - } else { - // remove detached components from paintableIdMap so they - // can be GC'ed - for (Iterator it = paintableIdMap.keySet().iterator(); it - .hasNext();) { - Component p = (Component) it.next(); - if (p.getApplication() == null) { - idPaintableMap.remove(paintableIdMap.get(p)); - it.remove(); - dirtyPaintabletSet.remove(p); - p.removeListener(this); - } + } else { + // remove detached components from paintableIdMap so they + // can be GC'ed + for (Iterator it = paintableIdMap.keySet().iterator(); it + .hasNext();) { + Component p = (Component) it.next(); + if (p.getApplication() == null) { + idPaintableMap.remove(paintableIdMap.get(p)); + it.remove(); + dirtyPaintabletSet.remove(p); + p.removeListener(this); } - paintables = getDirtyComponents(window); } - if (paintables != null) { - - // We need to avoid painting children before parent. - // This is ensured by ordering list by depth in component - // tree - Collections.sort(paintables, new Comparator() { - public int compare(Object o1, Object o2) { - Component c1 = (Component) o1; - Component c2 = (Component) o2; - int d1 = 0; - while (c1.getParent() != null) { - d1++; - c1 = c1.getParent(); - } - int d2 = 0; - while (c2.getParent() != null) { - d2++; - c2 = c2.getParent(); - } - if (d1 < d2) { - return -1; - } - if (d1 > d2) { - return 1; - } - return 0; + paintables = getDirtyComponents(window); + } + if (paintables != null) { + + // We need to avoid painting children before parent. + // This is ensured by ordering list by depth in component + // tree + Collections.sort(paintables, new Comparator() { + public int compare(Object o1, Object o2) { + Component c1 = (Component) o1; + Component c2 = (Component) o2; + int d1 = 0; + while (c1.getParent() != null) { + d1++; + c1 = c1.getParent(); } - }); - - for (final Iterator i = paintables.iterator(); i.hasNext();) { - final Paintable p = (Paintable) i.next(); - - // TODO CLEAN - if (p instanceof Window) { - final Window w = (Window) p; - if (w.getTerminal() == null) { - w.setTerminal(application.getMainWindow() - .getTerminal()); - } + int d2 = 0; + while (c2.getParent() != null) { + d2++; + c2 = c2.getParent(); } - /* - * This does not seem to happen in tk5, but remember - * this case: else if (p instanceof Component) { if - * (((Component) p).getParent() == null || ((Component) - * p).getApplication() == null) { // Component requested - * repaint, but is no // longer attached: skip - * paintablePainted(p); continue; } } - */ - - // TODO we may still get changes that have been - // rendered already (changes with only cached flag) - if (paintTarget.needsToBePainted(p)) { - paintTarget.startTag("change"); - paintTarget.addAttribute("format", "uidl"); - final String pid = getPaintableId(p); - paintTarget.addAttribute("pid", pid); - - p.paint(paintTarget); - - paintTarget.endTag("change"); + if (d1 < d2) { + return -1; + } + if (d1 > d2) { + return 1; + } + return 0; + } + }); + + for (final Iterator i = paintables.iterator(); i.hasNext();) { + final Paintable p = (Paintable) i.next(); + + // TODO CLEAN + if (p instanceof Window) { + final Window w = (Window) p; + if (w.getTerminal() == null) { + w.setTerminal(application.getMainWindow() + .getTerminal()); } - paintablePainted(p); } + /* + * This does not seem to happen in tk5, but remember this + * case: else if (p instanceof Component) { if (((Component) + * p).getParent() == null || ((Component) + * p).getApplication() == null) { // Component requested + * repaint, but is no // longer attached: skip + * paintablePainted(p); continue; } } + */ + + // TODO we may still get changes that have been + // rendered already (changes with only cached flag) + if (paintTarget.needsToBePainted(p)) { + paintTarget.startTag("change"); + paintTarget.addAttribute("format", "uidl"); + final String pid = getPaintableId(p); + paintTarget.addAttribute("pid", pid); + + p.paint(paintTarget); + + paintTarget.endTag("change"); + } + paintablePainted(p); } + } - paintTarget.close(); - outWriter.print("]"); // close changes + paintTarget.close(); + outWriter.print("]"); // close changes - outWriter.print(", \"meta\" : {"); - boolean metaOpen = false; + outWriter.print(", \"meta\" : {"); + boolean metaOpen = false; - if (repaintAll) { - metaOpen = true; - outWriter.write("\"repaintAll\":true"); - } + if (repaintAll) { + metaOpen = true; + outWriter.write("\"repaintAll\":true"); + } - // add meta instruction for client to set focus if it is set - final Paintable f = (Paintable) application.consumeFocus(); - if (f != null) { - if (metaOpen) { - outWriter.write(","); - } - outWriter.write("\"focus\":\"" + getPaintableId(f) + "\""); + // add meta instruction for client to set focus if it is set + final Paintable f = (Paintable) application.consumeFocus(); + if (f != null) { + if (metaOpen) { + outWriter.write(","); } + outWriter.write("\"focus\":\"" + getPaintableId(f) + "\""); + } - outWriter.print("}, \"resources\" : {"); + outWriter.print("}, \"resources\" : {"); - // Precache custom layouts - String themeName = window.getTheme(); - if (request.getParameter("theme") != null) { - themeName = request.getParameter("theme"); - } - if (themeName == null) { - themeName = "default"; + // Precache custom layouts + String themeName = window.getTheme(); + if (request.getParameter("theme") != null) { + themeName = request.getParameter("theme"); + } + if (themeName == null) { + themeName = "default"; + } + + // TODO We should only precache the layouts that are not + // cached already + int resourceIndex = 0; + for (final Iterator i = paintTarget.getPreCachedResources() + .iterator(); i.hasNext();) { + final String resource = (String) i.next(); + InputStream is = null; + try { + is = applicationServlet + .getServletContext() + .getResourceAsStream( + "/" + + ApplicationServlet.THEME_DIRECTORY_PATH + + themeName + "/" + resource); + } catch (final Exception e) { + e.printStackTrace(); } + if (is != null) { + + outWriter.print((resourceIndex++ > 0 ? ", " : "") + "\"" + + resource + "\" : "); + final StringBuffer layout = new StringBuffer(); - // TODO We should only precache the layouts that are not - // cached already - int resourceIndex = 0; - for (final Iterator i = paintTarget.getPreCachedResources() - .iterator(); i.hasNext();) { - final String resource = (String) i.next(); - InputStream is = null; try { - is = applicationServlet - .getServletContext() - .getResourceAsStream( - "/" - + ApplicationServlet.THEME_DIRECTORY_PATH - + themeName + "/" + resource); - } catch (final Exception e) { - e.printStackTrace(); - } - if (is != null) { - - outWriter.print((resourceIndex++ > 0 ? ", " : "") - + "\"" + resource + "\" : "); - final StringBuffer layout = new StringBuffer(); - - try { - final InputStreamReader r = new InputStreamReader( - is); - final char[] buffer = new char[20000]; - int charsRead = 0; - while ((charsRead = r.read(buffer)) > 0) { - layout.append(buffer, 0, charsRead); - } - r.close(); - } catch (final java.io.IOException e) { - System.err.println("Resource transfer failed: " - + request.getRequestURI() + ". (" - + e.getMessage() + ")"); + final InputStreamReader r = new InputStreamReader(is); + final char[] buffer = new char[20000]; + int charsRead = 0; + while ((charsRead = r.read(buffer)) > 0) { + layout.append(buffer, 0, charsRead); } - outWriter.print("\"" - + JsonPaintTarget.escapeJSON(layout.toString()) - + "\""); - } else { - System.err.println("CustomLayout " + "/" - + ApplicationServlet.THEME_DIRECTORY_PATH - + themeName + "/" + resource + " not found!"); + r.close(); + } catch (final java.io.IOException e) { + System.err.println("Resource transfer failed: " + + request.getRequestURI() + ". (" + + e.getMessage() + ")"); } + outWriter.print("\"" + + JsonPaintTarget.escapeJSON(layout.toString()) + + "\""); + } else { + System.err.println("CustomLayout " + "/" + + ApplicationServlet.THEME_DIRECTORY_PATH + + themeName + "/" + resource + " not found!"); } - outWriter.print("}"); + } + outWriter.print("}"); - printLocaleDeclarations(outWriter); + printLocaleDeclarations(outWriter); - outWriter.print("}]"); + outWriter.print("}]"); - outWriter.flush(); - outWriter.close(); - } - - out.flush(); - out.close(); - } catch (SocketException e) { - // Most likely client browser closed socket - System.err - .println("Warning: SocketException in CommunicationManager." - + " Most likely client (browser) closed socket."); - } catch (final Throwable e) { - e.printStackTrace(); - // Writes the error report to client - // FIXME breaks UIDL response, security shouldn't reveal stack trace - // to client side - final OutputStreamWriter w = new OutputStreamWriter(out); - final PrintWriter err = new PrintWriter(w); - err - .write("Application Internal Error"); - err.write("

" + e.toString() + "

\n");
-            e.printStackTrace(new PrintWriter(err));
-            err.write("\n
"); - err.close(); + outWriter.flush(); + outWriter.close(); } + + out.flush(); + out.close(); } /** @@ -569,7 +549,12 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { variable[VAR_TYPE].charAt(0), variable[VAR_VALUE])); } - owner.changeVariables(request, m); + try { + owner.changeVariables(request, m); + } catch (Exception e) { + handleChangeVariablesError(application2, + (Component) owner, e, m); + } } else { // Ignore variable change String msg = "Warning: Ignoring variable change for "; @@ -592,6 +577,47 @@ public class CommunicationManager implements Paintable.RepaintRequestListener { return success; } + public class ErrorHandlerErrorEvent implements ErrorEvent { + + private Throwable throwable; + + public ErrorHandlerErrorEvent(Throwable throwable) { + this.throwable = throwable; + } + + public Throwable getThrowable() { + return throwable; + } + + } + + private void handleChangeVariablesError(Application application, + Component owner, Exception e, Map m) { + boolean handled = false; + ChangeVariablesErrorEvent errorEvent = new ChangeVariablesErrorEvent( + owner, e, 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 application error handler and continue + * processing the actual error + */ + application.getErrorHandler().terminalError( + new ErrorHandlerErrorEvent(handlerException)); + handled = false; + } + } + + if (!handled) { + application.getErrorHandler().terminalError(errorEvent); + } + + } + private Object convertVariableValue(char variableType, String strValue) { Object val = null; switch (variableType) { diff --git a/src/com/itmill/toolkit/tests/tickets/Ticket2002.java b/src/com/itmill/toolkit/tests/tickets/Ticket2002.java new file mode 100644 index 0000000000..2e69a66745 --- /dev/null +++ b/src/com/itmill/toolkit/tests/tickets/Ticket2002.java @@ -0,0 +1,50 @@ +package com.itmill.toolkit.tests.tickets; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.data.util.MethodProperty; +import com.itmill.toolkit.ui.GridLayout; +import com.itmill.toolkit.ui.TextField; +import com.itmill.toolkit.ui.Window; + +public class Ticket2002 extends Application { + private Long long1 = new Long(1L); + private Long long2 = new Long(2L); + + public Long getLong1() { + return long1; + } + + public void setLong1(Long long1) { + this.long1 = long1; + } + + public Long getLong2() { + return long2; + } + + public void setLong2(Long long2) { + this.long2 = long2; + } + + public void init() { + Window w = new Window(getClass().getSimpleName()); + setMainWindow(w); + + GridLayout layout = new GridLayout(2, 2); + layout.setSpacing(true); + + TextField f1 = new TextField("Non-immediate/Long text field", + new MethodProperty(this, "long1")); + f1.setImmediate(false); + f1.setNullSettingAllowed(true); + TextField f2 = new TextField("Immediate/Long text field", + new MethodProperty(this, "long2")); + f2.setImmediate(true); + f2.setNullSettingAllowed(true); + + layout.addComponent(f1); + layout.addComponent(f2); + + w.setLayout(layout); + } +} diff --git a/src/com/itmill/toolkit/ui/AbstractField.java b/src/com/itmill/toolkit/ui/AbstractField.java index 71b1c4c140..70b17ad41a 100644 --- a/src/com/itmill/toolkit/ui/AbstractField.java +++ b/src/com/itmill/toolkit/ui/AbstractField.java @@ -21,6 +21,7 @@ import com.itmill.toolkit.terminal.CompositeErrorMessage; import com.itmill.toolkit.terminal.ErrorMessage; import com.itmill.toolkit.terminal.PaintException; import com.itmill.toolkit.terminal.PaintTarget; +import com.itmill.toolkit.terminal.Terminal; /** *

@@ -121,6 +122,8 @@ public abstract class AbstractField extends AbstractComponent implements Field, */ private boolean validationVisible = true; + private ComponentErrorHandler errorHandler = null; + /* Component basics ************************************************ */ /* @@ -1082,4 +1085,64 @@ public abstract class AbstractField extends AbstractComponent implements Field, } } + public interface ComponentErrorHandler { + /** + * 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; + } + + public boolean handleError(ComponentErrorEvent error) { + if (errorHandler != null) { + return errorHandler.handleComponentError(error); + } + return false; + + } + + /** + * Sets the current buffered source exception. + * + * @param currentBufferedSourceException + */ + public void setCurrentBufferedSourceException( + Buffered.SourceException currentBufferedSourceException) { + this.currentBufferedSourceException = currentBufferedSourceException; + requestRepaint(); + } + + public interface ComponentErrorEvent extends Terminal.ErrorEvent { + } + } \ No newline at end of file -- 2.39.5