From c60ca65f8f941fe3d80317747f5dc4f41c0e70d6 Mon Sep 17 00:00:00 2001 From: Marc Englund Date: Fri, 23 Jan 2009 09:48:33 +0000 Subject: Added a communication error system-message that is written to the client when the initial page is rendered. If any UIDL request fails, the message is shown. Fixes [2485] svn changeset:6624/svn branch:trunk --- src/com/itmill/toolkit/Application.java | 59 +++++++++++++++++----- .../gwt/client/ApplicationConfiguration.java | 20 ++++++++ .../terminal/gwt/client/ApplicationConnection.java | 44 +++++++++++++--- .../terminal/gwt/server/ApplicationServlet.java | 53 +++++++++++++++++-- 4 files changed, 155 insertions(+), 21 deletions(-) (limited to 'src/com') diff --git a/src/com/itmill/toolkit/Application.java b/src/com/itmill/toolkit/Application.java index fa4f5e9a9d..866a9ced51 100644 --- a/src/com/itmill/toolkit/Application.java +++ b/src/com/itmill/toolkit/Application.java @@ -1195,6 +1195,11 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener protected String sessionExpiredCaption = "Session Expired"; protected String sessionExpiredMessage = "Take note of any unsaved data, and click here to continue."; + protected String communicationErrorURL = null; + protected boolean communicationErrorNotificationEnabled = true; + protected String communicationErrorCaption = "Communication problem"; + protected String communicationErrorMessage = "Take note of any unsaved data, and click here to continue."; + protected String internalErrorURL = null; protected boolean internalErrorNotificationEnabled = true; protected String internalErrorCaption = "Internal Error"; @@ -1242,6 +1247,36 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener : null); } + /** + * @return the URL to load on null for restart + */ + public String getCommunicationErrorURL() { + return communicationErrorURL; + } + + /** + * @return true = notification enabled, false = notification disabled + */ + public boolean isCommunicationErrorNotificationEnabled() { + return communicationErrorNotificationEnabled; + } + + /** + * @return the notification caption, or null for no caption + */ + public String getCommunicationErrorCaption() { + return (communicationErrorNotificationEnabled ? communicationErrorCaption + : null); + } + + /** + * @return the notification message, or null for no message + */ + public String getCommunicationErrorMessage() { + return (communicationErrorNotificationEnabled ? communicationErrorMessage + : null); + } + /** * @return the URL to load on null for restart */ @@ -1313,26 +1348,26 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener *

*

* The default behavior is to show a notification, and restart the - * application the the user clicks the message.
- * Instead of restarting the application, you can set a specific URL that - * the user is taken to.
- * Setting both caption and message to null will restart the application (or - * go to the specified URL) without displaying a notification. - * set*NotificationEnabled(false) will achieve the same thing. + * application the the user clicks the message.
Instead of restarting + * the application, you can set a specific URL that the user is taken + * to.
Setting both caption and message to null will restart the + * application (or go to the specified URL) without displaying a + * notification. set*NotificationEnabled(false) will achieve the same thing. *

*

* The situations are: *

  • Session expired: the user session has expired, usually due to * inactivity.
  • + *
  • Communication error: the client failed to contact the server, or the + * server returned and invalid response.
  • *
  • Internal error: unhandled critical server error (e.g out of memory, * database crash) *
  • Out of sync: the client is not in sync with the server. E.g the user - * opens two windows showing the same application, and makes changes in one - * of the windows - the other window is no longer in sync, and (for - * instance) pressing a button that is no longer present in the UI will - * cause a out-of-sync -situation. You might want to disable the - * notification if silently ignoring a action (e.g button press) is not a - * problem in your application. + * opens two windows showing the same application, but the application does + * not support this and uses the same Window instance. When the user makes + * changes in one of the windows - the other window is no longer in sync, + * and (for instance) pressing a button that is no longer present in the UI + * will cause a out-of-sync -situation. *

    */ diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConfiguration.java b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConfiguration.java index 3fe4015b9d..919ab0807c 100644 --- a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConfiguration.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConfiguration.java @@ -14,6 +14,9 @@ public class ApplicationConfiguration { private String appUri; private JavaScriptObject versionInfo; private String windowName; + private String communicationErrorCaption; + private String communicationErrorMessage; + private String communicationErrorUrl; private static ArrayList unstartedApplications = new ArrayList(); private static ArrayList runningApplications = new ArrayList(); @@ -50,6 +53,18 @@ public class ApplicationConfiguration { return versionInfo; } + public String getCommunicationErrorCaption() { + return communicationErrorCaption; + } + + public String getCommunicationErrorMessage() { + return communicationErrorMessage; + } + + public String getCommunicationErrorUrl() { + return communicationErrorUrl; + } + private native void loadFromDOM() /*-{ @@ -69,6 +84,11 @@ public class ApplicationConfiguration { if(jsobj.versionInfo) { this.@com.itmill.toolkit.terminal.gwt.client.ApplicationConfiguration::versionInfo = jsobj.versionInfo; } + if(jsobj.comErrMsg) { + this.@com.itmill.toolkit.terminal.gwt.client.ApplicationConfiguration::communicationErrorCaption = jsobj.comErrMsg.caption; + this.@com.itmill.toolkit.terminal.gwt.client.ApplicationConfiguration::communicationErrorMessage = jsobj.comErrMsg.message; + this.@com.itmill.toolkit.terminal.gwt.client.ApplicationConfiguration::communicationErrorUrl = jsobj.comErrMsg.url; + } } else { $wnd.alert("Toolkit app failed to initialize: " + this.id); diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java index e9f4f1597b..d8dd3a5895 100755 --- a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java @@ -328,12 +328,13 @@ public class ApplicationConnection { if (!forceSync) { final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri); + // TODO enable timeout + // rb.setTimeoutMillis(timeoutMillis); rb.setHeader("Content-Type", "text/plain;charset=utf-8"); try { rb.sendRequest(requestData, new RequestCallback() { public void onError(Request request, Throwable exception) { - // TODO Better reporting to user - console.error("Got error"); + showCommunicationError(exception.getMessage()); endRequest(); if (!applicationRunning) { // start failed, let's try to start the next app @@ -343,6 +344,16 @@ public class ApplicationConnection { public void onResponseReceived(Request request, Response response) { + console.log("Server visit took " + + String.valueOf((new Date()).getTime() + - requestStartTime.getTime()) + "ms"); + + switch (response.getStatusCode()) { + case 0: + showCommunicationError("Invalid status code 0 (server down?)"); + return; + // TODO could add more cases + } if ("init".equals(uidl_security_key)) { // Read security key String key = response @@ -351,9 +362,6 @@ public class ApplicationConnection { uidl_security_key = key; } } - console.log("Server visit took " - + String.valueOf((new Date()).getTime() - - requestStartTime.getTime()) + "ms"); if (applicationRunning) { handleReceivedJSONMessage(response); } else { @@ -405,6 +413,30 @@ public class ApplicationConnection { } } + /** + * Shows the communication error notification. The 'details' only go to the + * console for now. + * + * @param details + * Optional details for debugging. + */ + private void showCommunicationError(String details) { + console.error("Communication error: " + details); + String html = ""; + if (configuration.getCommunicationErrorCaption() != null) { + html += "

    " + configuration.getCommunicationErrorCaption() + + "

    "; + } + if (configuration.getCommunicationErrorMessage() != null) { + html += "

    " + configuration.getCommunicationErrorMessage() + + "

    "; + } + INotification n = new INotification(1000 * 60 * 45); + n.addEventListener(new NotificationRedirect(configuration + .getCommunicationErrorUrl())); + n.show(html, INotification.CENTERED_TOP, INotification.STYLE_SYSTEM); + } + private native void syncSendForce(JavaScriptObject xmlHttpRequest, String uri, String requestData) /*-{ @@ -551,7 +583,7 @@ public class ApplicationConnection { json = JSONParser.parse(jsonText); } catch (final com.google.gwt.json.client.JSONException e) { endRequest(); - console.log(e.getMessage() + " - Original JSON-text:"); + showCommunicationError(e.getMessage() + " - Original JSON-text:"); console.log(jsonText); return; } diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java index d442b1ed17..471ef2877e 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java @@ -784,7 +784,8 @@ public class ApplicationServlet extends HttpServlet { */ private void writeAjaxPage(HttpServletRequest request, HttpServletResponse response, Window window, String themeName, - Application application) throws IOException, MalformedURLException { + Application application) throws IOException, MalformedURLException, + ServletException { // e.g portlets only want a html fragment boolean fragment = (request.getAttribute(REQUEST_FRAGMENT) != null); @@ -885,6 +886,15 @@ public class ApplicationServlet extends HttpServlet { // but indicates that it should not be used in CSS and such: appId = appId + appId.hashCode(); + // Get system messages + Application.SystemMessages systemMessages = null; + try { + systemMessages = getSystemMessages(); + } catch (SystemMessageException e) { + // failing to get the system messages is always a problem + throw new ServletException("CommunicationError!", e); + } + if (isGecko17(request)) { // special start page for gecko 1.7 versions. Firefox 1.0 is not // supported, but the hack is make it possible to use linux and @@ -923,7 +933,25 @@ public class ApplicationServlet extends HttpServlet { page.write(VERSION); page.write("\",applicationVersion:\""); page.write(application.getVersion()); - page.write("\"}"); + page.write("\"},"); + if (systemMessages != null) { + // Write the CommunicationError -message to client + String caption = systemMessages.getCommunicationErrorCaption(); + if (caption != null) { + caption = "\"" + caption + "\""; + } + String message = systemMessages.getCommunicationErrorMessage(); + if (message != null) { + message = "\"" + message + "\""; + } + String url = systemMessages.getCommunicationErrorURL(); + if (url != null) { + url = "\"" + url + "\""; + } + page.write("\"comErrMsg\": {" + "\"caption\":" + caption + "," + + "\"message\" : " + message + "," + "\"url\" : " + url + + "}"); + } page.write("};\n//]]>\n\n"); if (themeName != null) { @@ -978,7 +1006,26 @@ public class ApplicationServlet extends HttpServlet { page.write(VERSION); page.write("\",applicationVersion:\""); page.write(application.getVersion()); - page.write("\"}"); + page.write("\"},"); + if (systemMessages != null) { + // Write the CommunicationError -message to client + String caption = systemMessages.getCommunicationErrorCaption(); + if (caption != null) { + caption = "\"" + caption + "\""; + } + String message = systemMessages.getCommunicationErrorMessage(); + if (message != null) { + message = "\"" + message + "\""; + } + String url = systemMessages.getCommunicationErrorURL(); + if (url != null) { + url = "\"" + url + "\""; + } + + page.write("\"comErrMsg\": {" + "\"caption\":" + caption + "," + + "\"message\" : " + message + "," + "\"url\" : " + url + + "}"); + } page.write("};\n//]]>\n\n"); if (themeName != null) { -- cgit v1.2.3