summaryrefslogtreecommitdiffstats
path: root/src/com/itmill
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/itmill')
-rw-r--r--src/com/itmill/toolkit/Application.java145
-rwxr-xr-xsrc/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java54
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/client/ui/Notification.java40
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java108
-rw-r--r--src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java27
5 files changed, 283 insertions, 91 deletions
diff --git a/src/com/itmill/toolkit/Application.java b/src/com/itmill/toolkit/Application.java
index 6a7758f880..46a67df6df 100644
--- a/src/com/itmill/toolkit/Application.java
+++ b/src/com/itmill/toolkit/Application.java
@@ -169,11 +169,11 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener
private String logoutURL = null;
/**
- * URL where the user is redirected to when the Toolkit ApplicationServlet
- * session expires, or null if the application is just closed without
- * redirection.
+ * Experimental API, not finalized. The default SystemMessages (read-only).
+ * Change by overriding getSystemMessages() and returning
+ * CustomizedSystemMessages
*/
- private String expiredURL = null;
+ private static final SystemMessages DEFAULT_SYSTEM_MESSAGES = new SystemMessages();
private Focusable pendingFocus;
@@ -1032,28 +1032,18 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener
}
/**
- * Returns the URL where user is redirected to when the Toolkit
- * ApplicationServlet session expires. If the URL is <code>null</code>,
- * the application is closed normally and it shows a notification to the
- * client.
+ * Experimental API, not finalized. Gets the SystemMessages for this
+ * application. SystemMessages are used to notify the user of various
+ * critical situations that can occur, such as session expiration,
+ * client/server out of sync, and internal server error.
*
- * @return the URL.
- */
- public String getSessionExpiredURL() {
- return expiredURL;
- }
-
- /**
- * Sets the URL where user is redirected to when the Toolkit
- * ApplicationServlet session expires. If the URL is <code>null</code>,
- * the application is closed normally and it shows a notification to the
- * client.
+ * You can customize the messages by overriding this method and returning
+ * CustomizedSystemMessages.
*
- * @param expiredURL
- * the expiredURL to set.
+ * @return the SystemMessages for this application
*/
- public void setSessionExpiredURL(String expiredURL) {
- this.expiredURL = expiredURL;
+ public static SystemMessages getSystemMessages() {
+ return DEFAULT_SYSTEM_MESSAGES;
}
/**
@@ -1145,4 +1135,113 @@ public abstract class Application implements URIHandler, Terminal.ErrorListener
return "NONVERSIONED";
}
+ /**
+ * Experimental API, not finalized. Contains the system messages used to
+ * notify the user about various critical situations that can occur.
+ *
+ * Customize by overriding the static Application.getSystemMessages() and
+ * return CustomizedSystemMessages.
+ */
+ public static class SystemMessages {
+ protected String sessionExpiredURL = null;
+ protected String sessionExpiredCaption = "Session Expired";
+ protected String sessionExpiredMessage = "Take note of any unsaved data, and <u>click here</u> to continue.";
+
+ protected String internalErrorURL = null;
+ protected String internalErrorCaption = "Internal Error";
+ protected String internalErrorMessage = "Please notify the administrator.<br/>Take note of any unsaved data, and <u>click here</u> to continue.";
+
+ protected String outOfSyncURL = null;
+ protected String outOfSyncCaption = "Out of sync";
+ protected String outOfSyncMessage = "Something has caused us to be out of sync with the server.<br/>Take note of any unsaved data, and <u>click here</u> to re-sync.";
+
+ private SystemMessages() {
+
+ }
+
+ public String getSessionExpiredURL() {
+ return sessionExpiredURL;
+ }
+
+ public String getSessionExpiredCaption() {
+ return sessionExpiredCaption;
+ }
+
+ public String getSessionExpiredMessage() {
+ return sessionExpiredMessage;
+ }
+
+ public String getInternalErrorURL() {
+ return internalErrorURL;
+ }
+
+ public String getInternalErrorCaption() {
+ return internalErrorCaption;
+ }
+
+ public String getInternalErrorMessage() {
+ return internalErrorMessage;
+ }
+
+ public String getOutOfSyncURL() {
+ return outOfSyncURL;
+ }
+
+ public String getOutOfSyncCaption() {
+ return outOfSyncCaption;
+ }
+
+ public String getOutOfSyncMessage() {
+ return outOfSyncMessage;
+ }
+
+ }
+
+ /**
+ * Experimental API, not finalized. Contains the system messages used to
+ * notify the user about various critical situations that can occur.
+ *
+ * Customize by overriding the static Application.getSystemMessages() and
+ * return CustomizedSystemMessages.
+ */
+ public static class CustomizedSystemMessages extends SystemMessages {
+
+ public void setSessionExpiredURL(String sessionExpiredURL) {
+ this.sessionExpiredURL = sessionExpiredURL;
+ }
+
+ public void setSessionExpiredCaption(String sessionExpiredCaption) {
+ this.sessionExpiredCaption = sessionExpiredCaption;
+ }
+
+ public void setSessionExpiredMessage(String sessionExpiredMessage) {
+ this.sessionExpiredMessage = sessionExpiredMessage;
+ }
+
+ public void setInternalErrorURL(String internalErrorURL) {
+ this.internalErrorURL = internalErrorURL;
+ }
+
+ public void setInternalErrorCaption(String internalErrorCaption) {
+ this.internalErrorCaption = internalErrorCaption;
+ }
+
+ public void setInternalErrorMessage(String internalErrorMessage) {
+ this.internalErrorMessage = internalErrorMessage;
+ }
+
+ public void setOutOfSyncURL(String outOfSyncURL) {
+ this.outOfSyncURL = outOfSyncURL;
+ }
+
+ public void setOutOfSyncCaption(String outOfSyncCaption) {
+ this.outOfSyncCaption = outOfSyncCaption;
+ }
+
+ public void setOutOfSyncMessage(String outOfSyncMessage) {
+ this.outOfSyncMessage = outOfSyncMessage;
+ }
+
+ }
+
} \ No newline at end of file
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java
index 32c48bdb18..e794da776f 100755
--- a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java
@@ -34,6 +34,7 @@ import com.google.gwt.user.client.ui.Widget;
import com.itmill.toolkit.terminal.gwt.client.ui.ContextMenu;
import com.itmill.toolkit.terminal.gwt.client.ui.IView;
import com.itmill.toolkit.terminal.gwt.client.ui.Notification;
+import com.itmill.toolkit.terminal.gwt.client.ui.Notification.HideEvent;
/**
* Entry point classes define <code>onModuleLoad()</code>.
@@ -421,11 +422,28 @@ public class ApplicationConnection {
}
if (meta.containsKey("appError")) {
JSONObject error = meta.get("appError").isObject();
- String caption = error.get("caption").isString().stringValue();
- String message = error.get("message").isString().stringValue();
- String html = "<h1>" + caption + "</h1><p>" + message + "</p>";
- new Notification(Notification.DELAY_FOREVER).show(html,
- Notification.CENTERED, "error");
+ JSONValue val = error.get("caption");
+ String html = "";
+ if (val.isString() != null) {
+ html += "<h1>" + val.isString().stringValue() + "</h1>";
+ }
+ val = error.get("message");
+ if (val.isString() != null) {
+ html += "<p>" + val.isString().stringValue() + "</p>";
+ }
+ val = error.get("url");
+ String url = null;
+ if (val.isString() != null) {
+ url = val.isString().stringValue();
+ }
+
+ if (html.length() != 0) {
+ Notification n = new Notification(1000 * 60 * 45); // 45min
+ n.addEventListener(new NotificationRedirect(url));
+ n.show(html, Notification.CENTERED_TOP, "system");
+ } else {
+ redirect(url);
+ }
applicationRunning = false;
}
}
@@ -438,10 +456,14 @@ public class ApplicationConnection {
endRequest();
}
- // Redirect browser
+ // Redirect browser, null reloads current page
private static native void redirect(String url)
/*-{
- $wnd.location = url;
+ if (url) {
+ $wnd.location = url;
+ } else {
+ $wnd.location = $wnd.location;
+ }
}-*/;
public void registerPaintable(String id, Paintable paintable) {
@@ -763,4 +785,22 @@ public class ApplicationConnection {
public String getTheme() {
return view.getTheme();
}
+
+ /**
+ * Listens for Notification hide event, and redirects. Used for system
+ * messages, such as session expired.
+ *
+ */
+ private class NotificationRedirect implements Notification.EventListener {
+ String url;
+
+ NotificationRedirect(String url) {
+ this.url = url;
+ }
+
+ public void notificationHidden(HideEvent event) {
+ redirect(url);
+ }
+
+ }
}
diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ui/Notification.java b/src/com/itmill/toolkit/terminal/gwt/client/ui/Notification.java
index 8a703a7290..c66f8a635c 100644
--- a/src/com/itmill/toolkit/terminal/gwt/client/ui/Notification.java
+++ b/src/com/itmill/toolkit/terminal/gwt/client/ui/Notification.java
@@ -4,6 +4,10 @@
package com.itmill.toolkit.terminal.gwt.client.ui;
+import java.util.ArrayList;
+import java.util.EventObject;
+import java.util.Iterator;
+
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
@@ -40,6 +44,8 @@ public class Notification extends ToolkitOverlay {
private String temporaryStyle;
+ private ArrayList listeners;
+
public Notification() {
setStylePrimaryName(STYLENAME);
sinkEvents(Event.ONCLICK);
@@ -94,7 +100,6 @@ public class Notification extends ToolkitOverlay {
}
public void show(int position, String style) {
- hide();
setOpacity(getElement(), startOpacity);
if (style != null) {
temporaryStyle = style;
@@ -113,6 +118,7 @@ public class Notification extends ToolkitOverlay {
temporaryStyle = null;
}
super.hide();
+ fireEvent(new HideEvent(this));
}
public void fade() {
@@ -235,4 +241,36 @@ public class Notification extends ToolkitOverlay {
return true;
}
+ public void addEventListener(EventListener listener) {
+ if (listeners == null) {
+ listeners = new ArrayList();
+ }
+ listeners.add(listener);
+ }
+
+ public void removeEventListener(EventListener listener) {
+ if (listeners == null) {
+ return;
+ }
+ listeners.remove(listener);
+ }
+
+ private void fireEvent(HideEvent event) {
+ if (listeners != null) {
+ for (Iterator it = listeners.iterator(); it.hasNext();) {
+ EventListener l = (EventListener) it.next();
+ l.notificationHidden(event);
+ }
+ }
+ }
+
+ public class HideEvent extends EventObject {
+ public HideEvent(Object source) {
+ super(source);
+ }
+ }
+
+ public interface EventListener extends java.util.EventListener {
+ public void notificationHidden(HideEvent event);
+ }
}
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
index d0ba830307..0ac7a3f206 100644
--- a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
+++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java
@@ -12,6 +12,7 @@ import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collection;
@@ -500,39 +501,47 @@ public class ApplicationServlet extends HttpServlet {
}
} catch (final SessionExpired e) {
- // Session has expired
- // Get new application so we can fetch the sessionExpiredURL
- Application app = null;
+ // Session has expired, notify user
+ Application.SystemMessages ci = Application.getSystemMessages();
try {
- app = (Application) applicationClass.newInstance();
- app.init();
- } catch (InstantiationException e1) {
- // Should not happen
- e1.printStackTrace();
- } catch (IllegalAccessException e1) {
- // Should not happen
- e1.printStackTrace();
+ Method m = applicationClass
+ .getMethod("getSystemMessages", null);
+ ci = (Application.CustomizedSystemMessages) m
+ .invoke(null, null);
+ } catch (Exception e2) {
+ // Not critical, but something is still wrong; print stacktrace
+ e2.printStackTrace();
}
- // Redirect if expiredURL is found
- if (app != null && app.getSessionExpiredURL() != null) {
- if (UIDLrequest) {
- redirectUidlRequest(request, response, app
- .getSessionExpiredURL());
- } else {
- response.sendRedirect(app.getSessionExpiredURL());
- }
+ if (!UIDLrequest) {
+ // 'plain' http req - e.g. browser reload;
+ // just go ahead redirect the browser
+ response.sendRedirect(ci.getSessionExpiredURL());
} else {
- // Else show a notification to the client
- criticalNotification(request, response,
- "Your session has expired.");
+ // send uidl redirect
+ criticalNotification(request, response, ci
+ .getSessionExpiredCaption(), ci
+ .getSessionExpiredMessage(), ci.getSessionExpiredURL());
}
+
} catch (final Throwable e) {
e.printStackTrace();
// if this was an UIDL request, response UIDL back to client
if (UIDLrequest) {
- criticalNotification(request, response,
- "Internal error. Please notify administrator.");
+ Application.SystemMessages ci = Application.getSystemMessages();
+ try {
+ Method m = applicationClass.getMethod("getSystemMessages",
+ null);
+ ci = (Application.CustomizedSystemMessages) m.invoke(null,
+ null);
+ } catch (Exception e2) {
+ // Not critical, but something is still wrong; print
+ // stacktrace
+ e2.printStackTrace();
+ }
+ criticalNotification(request, response, ci
+ .getInternalErrorCaption(), ci
+ .getInternalErrorMessage(), ci.getInternalErrorURL());
} else {
// Re-throw other exceptions
throw new ServletException(e);
@@ -591,31 +600,6 @@ public class ApplicationServlet extends HttpServlet {
}
/**
- * Redirect an UIDL request to move to a new URL.
- *
- * @param request
- * the HTTP request instance.
- * @param response
- * the HTTP response to write to.
- * @param url
- * the URL to redirect to.
- * @throws IOException
- * if the writing failed due to input/output error.
- */
- private void redirectUidlRequest(HttpServletRequest request,
- HttpServletResponse response, String url) throws IOException {
- // Set the response type
- response.setContentType("application/json; charset=UTF-8");
- final ServletOutputStream out = response.getOutputStream();
- final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
- new OutputStreamWriter(out, "UTF-8")));
- outWriter.print("for(;;);[{\"redirect\":{\"url\":\"" + url + "\"}}]");
- outWriter.flush();
- outWriter.close();
- out.flush();
- }
-
- /**
* Send notification to client's application. Used to notify client of
* critical errors and session expiration due to long inactivity. Server has
* no knowledge of what application client refers to.
@@ -625,19 +609,32 @@ public class ApplicationServlet extends HttpServlet {
* @param response
* the HTTP response to write to.
* @param caption
- * for the notification message
+ * for the notification
+ * @param message
+ * for the notification
+ * @param url
+ * url to load after message, null for current page
* @throws IOException
* if the writing failed due to input/output error.
*/
- private void criticalNotification(HttpServletRequest request,
- HttpServletResponse response, String caption) throws IOException {
+ void criticalNotification(HttpServletRequest request,
+ HttpServletResponse response, String caption, String message,
+ String url) throws IOException {
// clients JS app is still running, but server application either
// no longer exists or it might fail to perform reasonably.
// send a notification to client's application and link how
// to "restart" application.
- // TODO message should be localized
+ if (caption != null) {
+ caption = "\"" + caption + "\"";
+ }
+ if (message != null) {
+ message = "\"" + message + "\"";
+ }
+ if (url != null) {
+ url = "\"" + url + "\"";
+ }
// Set the response type
response.setContentType("application/json; charset=UTF-8");
@@ -645,9 +642,8 @@ public class ApplicationServlet extends HttpServlet {
final PrintWriter outWriter = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(out, "UTF-8")));
outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {"
- + "\"appError\": {" + "\"caption\":\"" + caption + "\","
- + "\"message\" : \"<br />You can click your browser's"
- + " refresh button to restart your application.\""
+ + "\"appError\": {" + "\"caption\":" + caption + ","
+ + "\"message\" : " + message + "," + "\"url\" : " + url
+ "}}, \"resources\": {}, \"locales\":[]}]");
outWriter.flush();
outWriter.close();
diff --git a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
index 1fb98fa549..2e23e21853 100644
--- a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
+++ b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java
@@ -35,6 +35,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.itmill.toolkit.Application;
+import com.itmill.toolkit.Application.SystemMessages;
import com.itmill.toolkit.external.org.apache.commons.fileupload.FileItemIterator;
import com.itmill.toolkit.external.org.apache.commons.fileupload.FileItemStream;
import com.itmill.toolkit.external.org.apache.commons.fileupload.FileUploadException;
@@ -241,7 +242,15 @@ public class CommunicationManager implements Paintable.RepaintRequestListener {
}
// Change all variables based on request parameters
- handleVariables(request, application);
+ if (!handleVariables(request, application)) {
+ // var inconsistency; the client is probably out-of-sync
+ SystemMessages ci = application.getSystemMessages();
+ applicationServlet.criticalNotification(request, response,
+ ci.getOutOfSyncCaption(), ci.getOutOfSyncMessage(),
+ ci.getOutOfSyncURL());
+ // need to do a repaint all after this
+ return;
+ }
// Removes application if it has stopped during variable changes
if (!application.isRunning()) {
@@ -462,9 +471,18 @@ public class CommunicationManager implements Paintable.RepaintRequestListener {
}
}
- private Map handleVariables(HttpServletRequest request,
+ /**
+ * If this method returns false, something was submitted that we did not
+ * expect; this is probably due to the client being out-of-sync and sending
+ * variable changes for non-existing pids
+ *
+ * @param request
+ * @param application2
+ * @return true if successful, false if there was an inconsistency
+ */
+ private boolean handleVariables(HttpServletRequest request,
Application application2) {
-
+ boolean success = true;
final Map params = new HashMap(request.getParameterMap());
final String changes = (String) ((params.get("changes") instanceof String[]) ? ((String[]) params
.get("changes"))[0]
@@ -531,13 +549,14 @@ public class CommunicationManager implements Paintable.RepaintRequestListener {
} else {
msg += "non-existent component, VAR_PID="
+ variable[VAR_PID];
+ success = false;
}
System.err.println(msg);
continue;
}
}
}
- return params;
+ return success;
}
private Object convertVariableValue(char variableType, String strValue) {