/**
* Mapping from window name to window instance.
*/
- private final Hashtable windows = new Hashtable();
+ private final Hashtable<String, Window> windows = new Hashtable<String, Window>();
/**
* Main window of the application.
* The ID of the portlet window that this application runs in.
*/
private String portletWindowId;
-
+
/**
* Name of the theme currently used by the application.
*/
/**
* List of listeners listening user changes.
*/
- private LinkedList userChangeListeners = null;
+ private LinkedList<UserChangeListener> userChangeListeners = null;
/**
* Window attach listeners.
*/
- private LinkedList windowAttachListeners = null;
+ private LinkedList<WindowAttachListener> windowAttachListeners = null;
/**
* Window detach listeners.
*/
- private LinkedList windowDetachListeners = null;
+ private LinkedList<WindowDetachListener> windowDetachListeners = null;
/**
* Application resource mapping: key <-> resource.
*/
- private final Hashtable resourceKeyMap = new Hashtable();
+ private final Hashtable<ApplicationResource, String> resourceKeyMap = new Hashtable<ApplicationResource, String>();
- private final Hashtable keyResourceMap = new Hashtable();
+ private final Hashtable<String, ApplicationResource> keyResourceMap = new Hashtable<String, ApplicationResource>();
private long lastResourceKeyNumber = 0;
public String getPortletWindowId() {
return portletWindowId;
}
-
+
// TODO Document me!
public void setPortletWindowId(String portletWindowId) {
this.portletWindowId = portletWindowId;
}
-
+
+ // TODO Document me!
+ public static interface ResourceURLGenerator {
+
+ public String generateResourceURL(ApplicationResource resource,
+ String mapKey);
+
+ public boolean isResourceURL(URL context, String relativeUri);
+
+ public String getMapKey(URL context, String relativeUri);
+
+ }
+
+ /*
+ * Default resource URL generator for servlets
+ */
+ private static ResourceURLGenerator defaultResourceURLGenerator = new ResourceURLGenerator() {
+ public String generateResourceURL(ApplicationResource resource,
+ String mapKey) {
+
+ final String filename = resource.getFilename();
+ if (filename == null) {
+ return "APP/" + mapKey + "/";
+ } else {
+ return "APP/" + mapKey + "/" + filename;
+ }
+
+ }
+
+ public boolean isResourceURL(URL context, String relativeUri) {
+ // If the relative uri is null, we are ready
+ if (relativeUri == null) {
+ return false;
+ }
+
+ // Resolves the prefix
+ String prefix = relativeUri;
+ final int index = relativeUri.indexOf('/');
+ if (index >= 0) {
+ prefix = relativeUri.substring(0, index);
+ }
+
+ // Handles the resource requests
+ return (prefix.equals("APP"));
+ }
+
+ public String getMapKey(URL context, String relativeUri) {
+ final int index = relativeUri.indexOf('/');
+ final int next = relativeUri.indexOf('/', index + 1);
+ if (next < 0) {
+ return null;
+ }
+ return relativeUri.substring(index + 1, next);
+ };
+
+ };
+
+ private ResourceURLGenerator resourceURLGenerator = defaultResourceURLGenerator;
+
+ public ResourceURLGenerator getResourceURLGenerator() {
+ return resourceURLGenerator;
+ }
+
+ public void setResourceURLGenerator(
+ ResourceURLGenerator resourceURLGenerator) {
+ if (resourceURLGenerator == null)
+ this.resourceURLGenerator = defaultResourceURLGenerator;
+ else
+ this.resourceURLGenerator = resourceURLGenerator;
+ }
+
/**
* <p>
* Gets a window by name. Returns <code>null</code> if the application is
*
* @return the Unmodifiable collection of windows.
*/
- public Collection getWindows() {
+ public Collection<Window> getWindows() {
return Collections.unmodifiableCollection(windows.values());
}
*/
public void setTheme(String theme) {
// Collect list of windows not having the current or future theme
- final LinkedList toBeUpdated = new LinkedList();
+ final LinkedList<Window> toBeUpdated = new LinkedList<Window>();
final String oldAppTheme = getTheme();
- for (final Iterator i = getWindows().iterator(); i.hasNext();) {
- final Window w = (Window) i.next();
+ for (final Iterator<Window> i = getWindows().iterator(); i.hasNext();) {
+ final Window w = i.next();
final String windowTheme = w.getTheme();
if ((windowTheme == null)
|| (!windowTheme.equals(theme) && windowTheme
this.theme = theme;
// Ask windows to update themselves
- for (final Iterator i = toBeUpdated.iterator(); i.hasNext();) {
- ((Window) i.next()).requestRepaint();
+ for (final Iterator<Window> i = toBeUpdated.iterator(); i.hasNext();) {
+ i.next().requestRepaint();
}
}
* the keys in the default property list.
*
*/
- public Enumeration getPropertyNames() {
+ public Enumeration<?> getPropertyNames() {
return properties.propertyNames();
}
return null;
}
- final String filename = resource.getFilename();
- if (filename == null) {
- return "APP/" + key + "/";
- } else {
- return "APP/" + key + "/" + filename;
- }
+ return resourceURLGenerator.generateResourceURL(resource, key);
}
/**
*/
public DownloadStream handleURI(URL context, String relativeUri) {
- // If the relative uri is null, we are ready
- if (relativeUri == null) {
- return null;
- }
-
- // Resolves the prefix
- String prefix = relativeUri;
- final int index = relativeUri.indexOf('/');
- if (index >= 0) {
- prefix = relativeUri.substring(0, index);
- }
-
- // Handles the resource requests
- if (prefix.equals("APP")) {
+ if (resourceURLGenerator.isResourceURL(context, relativeUri)) {
// Handles the resource request
- final int next = relativeUri.indexOf('/', index + 1);
- if (next < 0) {
- return null;
- }
- final String key = relativeUri.substring(index + 1, next);
+ final String key = resourceURLGenerator.getMapKey(context,
+ relativeUri);
final ApplicationResource resource = (ApplicationResource) keyResourceMap
.get(key);
if (resource != null) {
*/
public void addListener(UserChangeListener listener) {
if (userChangeListeners == null) {
- userChangeListeners = new LinkedList();
+ userChangeListeners = new LinkedList<UserChangeListener>();
}
userChangeListeners.add(listener);
}
*/
public void addListener(WindowAttachListener listener) {
if (windowAttachListeners == null) {
- windowAttachListeners = new LinkedList();
+ windowAttachListeners = new LinkedList<WindowAttachListener>();
}
windowAttachListeners.add(listener);
}
*/
public void addListener(WindowDetachListener listener) {
if (windowDetachListeners == null) {
- windowDetachListeners = new LinkedList();
+ windowDetachListeners = new LinkedList<WindowDetachListener>();
}
windowDetachListeners.add(listener);
}
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
+import java.net.URL;
import java.security.GeneralSecurityException;
import java.util.Collection;
import java.util.Date;
import com.vaadin.Application;
import com.vaadin.Application.SystemMessages;
import com.vaadin.external.org.apache.commons.fileupload.portlet.PortletFileUpload;
+import com.vaadin.terminal.ApplicationResource;
+import com.vaadin.terminal.DownloadStream;
import com.vaadin.terminal.Terminal;
-import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.ui.Window;
/**
* to try to integrate the common parts into a shared super class.
*/
- // TODO Close application when portlet window is closed
+ // TODO Can we close the application when the portlet is removed? Do we know
+ // when the portlet is removed?
- // TODO What happens when the portlet window is resized?
+ // TODO What happens when the portlet window is resized? Do we know when the
+ // window is resized?
private Properties applicationProperties;
private boolean productionMode = false;
+ private static class PortletResourceURLGenerator implements
+ Application.ResourceURLGenerator {
+
+ private final MimeResponse response;
+
+ public PortletResourceURLGenerator(MimeResponse response) {
+ this.response = response;
+ }
+
+ public boolean isResourceURL(URL context, String relativeUri) {
+ // If the relative uri is null, we are ready
+ if (relativeUri == null) {
+ return false;
+ }
+
+ // Resolves the prefix
+ String prefix = relativeUri;
+ final int index = relativeUri.indexOf('/');
+ if (index >= 0) {
+ prefix = relativeUri.substring(0, index);
+ }
+
+ // Handles the resource requests
+ return (prefix.equals("APP"));
+ }
+
+ public String getMapKey(URL context, String relativeUri) {
+ final int index = relativeUri.indexOf('/');
+ final int next = relativeUri.indexOf('/', index + 1);
+ if (next < 0) {
+ return null;
+ }
+ return relativeUri.substring(index + 1, next);
+ }
+
+ public String generateResourceURL(ApplicationResource resource,
+ String mapKey) {
+ ResourceURL resourceURL = response.createResourceURL();
+ final String filename = resource.getFilename();
+ if (filename == null) {
+ resourceURL.setResourceID("APP/" + mapKey + "/");
+ } else {
+ resourceURL.setResourceID("APP/" + mapKey + "/" + filename);
+ }
+ return resourceURL.toString();
+ }
+ };
+
@SuppressWarnings("unchecked")
@Override
public void init(PortletConfig config) throws PortletException {
}
enum RequestType {
- FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, UNKNOWN;
+ FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, UNKNOWN;
}
protected RequestType getRequestType(PortletRequest request) {
} else if (request instanceof ResourceRequest) {
if (isUIDLRequest((ResourceRequest) request)) {
return RequestType.UIDL;
- } else if (isStaticResourceRequest((ResourceRequest) request)) {
+ } else if (isApplicationResourceRequest((ResourceRequest) request)) {
+ return RequestType.APPLICATION_RESOURCE;
+ } else {
return RequestType.STATIC_FILE;
}
} else if (request instanceof ActionRequest) {
return RequestType.UNKNOWN;
}
- private boolean isStaticResourceRequest(ResourceRequest request) {
- String resourceID = request.getResourceID();
- if (resourceID != null && !resourceID.startsWith("/VAADIN/")) {
- return true;
- }
- return false;
+ private boolean isApplicationResourceRequest(ResourceRequest request) {
+ return request.getResourceID() != null
+ && request.getResourceID().startsWith("APP");
}
private boolean isUIDLRequest(ResourceRequest request) {
if (application == null) {
return;
}
+ if (response instanceof MimeResponse) {
+ application
+ .setResourceURLGenerator(new PortletResourceURLGenerator(
+ (MimeResponse) response));
+ }
/*
* Get or create an application context and an application manager
applicationManager.handleUidlRequest((ResourceRequest) request,
(ResourceResponse) response, this);
return;
- } else if (requestType == RequestType.RENDER) {
- /*
- * Removes the application if it has stopped
- */
- if (!application.isRunning()) {
- endApplication(request, response, application);
- return;
- }
+ }
- /*
- * Always use the main window when running inside a portlet.
- */
- Window window = application.getMainWindow();
- if (window == null) {
- throw new PortletException(ERROR_NO_WINDOW_FOUND);
- }
+ /*
+ * Removes the application if it has stopped
+ */
+ if (!application.isRunning()) {
+ endApplication(request, response, application);
+ return;
+ }
- /*
- * Sets terminal type for the window, if not already set
- */
- if (window.getTerminal() == null) {
- window.setTerminal(applicationContext.getBrowser());
- }
+ /*
+ * Always use the main window when running inside a portlet.
+ */
+ Window window = application.getMainWindow();
+ if (window == null) {
+ throw new PortletException(ERROR_NO_WINDOW_FOUND);
+ }
- /*
- * Handle parameters
- */
- final Map<String, String[]> parameters = request
- .getParameterMap();
- if (window != null && parameters != null) {
- window.handleParameters(parameters);
- }
+ /*
+ * Sets terminal type for the window, if not already set
+ */
+ if (window.getTerminal() == null) {
+ window.setTerminal(applicationContext.getBrowser());
+ }
+
+ /*
+ * Handle parameters
+ */
+ final Map<String, String[]> parameters = request.getParameterMap();
+ if (window != null && parameters != null) {
+ window.handleParameters(parameters);
+ }
+ if (requestType == RequestType.APPLICATION_RESOURCE) {
+ handleURI(applicationManager, window,
+ (ResourceRequest) request, (ResourceResponse) response);
+ } else if (requestType == RequestType.RENDER) {
/*
* Send initial AJAX page that kickstarts the Vaadin application
*/
(RenderResponse) response, window, application);
}
} catch (final SessionExpired e) {
- // Session has expired, notify user
- handleServiceSessionExpired(request, response);
+ // TODO Figure out a better way to deal with SessionExpired
+ // -exceptions
+ System.err.println("Session has expired");
+ e.printStackTrace(System.err);
} catch (final GeneralSecurityException e) {
- handleServiceSecurityException(request, response);
+ // TODO Figure out a better way to deal with
+ // GeneralSecurityExceptions
+ System.err
+ .println("General security exception, should never happen");
+ e.printStackTrace(System.err);
} catch (final Throwable e) {
handleServiceException(request, response, application, e);
} finally {
}
}
+ private boolean handleURI(PortletCommunicationManager applicationManager,
+ Window window, ResourceRequest request, ResourceResponse response)
+ throws IOException {
+ // Handles the URI
+ DownloadStream download = applicationManager.handleURI(window, request,
+ response, this);
+
+ // A download request
+ if (download != null) {
+ // Client downloads an resource
+ handleDownload(download, request, response);
+ return true;
+ }
+
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void handleDownload(DownloadStream stream, ResourceRequest request,
+ ResourceResponse response) throws IOException {
+
+ if (stream.getParameter("Location") != null) {
+ response.setProperty(ResourceResponse.HTTP_STATUS_CODE, Integer
+ .toString(HttpServletResponse.SC_MOVED_TEMPORARILY));
+ response.setProperty("Location", stream.getParameter("Location"));
+ return;
+ }
+
+ // Download from given stream
+ final InputStream data = stream.getStream();
+ if (data != null) {
+
+ // Sets content type
+ response.setContentType(stream.getContentType());
+
+ // Sets cache headers
+ final long cacheTime = stream.getCacheTime();
+ if (cacheTime <= 0) {
+ response.setProperty("Cache-Control", "no-cache");
+ response.setProperty("Pragma", "no-cache");
+ response.setProperty("Expires", "0");
+ } else {
+ response.setProperty("Cache-Control", "max-age=" + cacheTime
+ / 1000);
+ response.setProperty("Expires", "" + System.currentTimeMillis()
+ + cacheTime);
+ response.setProperty("Pragma", "cache"); // Required to apply
+ // caching in some
+ // Tomcats
+ }
+
+ // Copy download stream parameters directly
+ // to HTTP headers.
+ final Iterator i = stream.getParameterNames();
+ if (i != null) {
+ while (i.hasNext()) {
+ final String param = (String) i.next();
+ response.setProperty(param, stream.getParameter(param));
+ }
+ }
+
+ // suggest local filename from DownloadStream if Content-Disposition
+ // not explicitly set
+ String contentDispositionValue = stream
+ .getParameter("Content-Disposition");
+ if (contentDispositionValue == null) {
+ contentDispositionValue = "filename=\"" + stream.getFileName()
+ + "\"";
+ response.setProperty("Content-Disposition",
+ contentDispositionValue);
+ }
+
+ int bufferSize = stream.getBufferSize();
+ if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) {
+ bufferSize = DEFAULT_BUFFER_SIZE;
+ }
+ final byte[] buffer = new byte[bufferSize];
+ int bytesRead = 0;
+
+ final OutputStream out = response.getPortletOutputStream();
+
+ while ((bytesRead = data.read(buffer)) > 0) {
+ out.write(buffer, 0, bytesRead);
+ out.flush();
+ }
+ out.close();
+ }
+ }
+
private void serveStaticResources(ResourceRequest request,
ResourceResponse response) throws IOException, PortletException {
final String resourceID = request.getResourceID();
} else {
widgetset = sharedWidgetset;
}
-
- // TODO Currently, we can only load widgetsets and themes from the portal
-
+
+ // TODO Currently, we can only load widgetsets and themes from the
+ // portal
+
String themeName = getThemeForWindow(request, window);
String widgetsetURL = getWidgetsetURL(widgetset);
page.write("};\n</script>\n");
page.write("<script type=\"text/javascript\">\n");
- page.write("//<![CDATA[\n");
+ // page.write("//<![CDATA[\n");
page.write("if(!vaadin.themesLoaded['" + themeName + "']) {\n");
page.write("var stylesheet = document.createElement('link');\n");
page.write("stylesheet.setAttribute('rel', 'stylesheet');\n");
page
.write("document.getElementsByTagName('head')[0].appendChild(stylesheet);\n");
page.write("vaadin.themesLoaded['" + themeName + "'] = true;\n}\n");
- page.write("//]]>\n</script>\n");
+ page.write("</script>\n");
// TODO Warn if widgetset has not been loaded after 15 seconds
return getClass().getClassLoader();
}
- private boolean isOnUnloadRequest(PortletRequest request) {
- return request.getParameter(ApplicationConnection.PARAM_UNLOADBURST) != null;
- }
-
/**
* Get system messages from the current application class
*
return Application.getSystemMessages();
}
- void handleServiceSessionExpired(PortletRequest request,
- PortletResponse response) throws IOException, PortletException {
-
- if (isOnUnloadRequest(request)) {
- /*
- * Request was an unload request (e.g. window close event) and the
- * client expects no response if it fails.
- */
- return;
- }
-
- try {
- Application.SystemMessages ci = getSystemMessages();
- if (getRequestType(request) != RequestType.UIDL) {
- // 'plain' http req - e.g. browser reload;
- // just go ahead redirect the browser
- if (response instanceof ActionResponse) {
- ((ActionResponse) response).sendRedirect(ci
- .getSessionExpiredURL());
- } else {
- // TODO What to do if we are e.g. rendering?
- }
- } else {
- /*
- * Session must be invalidated before criticalNotification as it
- * commits the response.
- */
- request.getPortletSession().invalidate();
-
- // send uidl redirect
- criticalNotification(request, (ResourceResponse) response, ci
- .getSessionExpiredCaption(), ci
- .getSessionExpiredMessage(), null, ci
- .getSessionExpiredURL());
-
- }
- } catch (SystemMessageException ee) {
- throw new PortletException(ee);
- }
-
- }
-
- private void handleServiceSecurityException(PortletRequest request,
- PortletResponse response) throws IOException, PortletException {
- if (isOnUnloadRequest(request)) {
- /*
- * Request was an unload request (e.g. window close event) and the
- * client expects no response if it fails.
- */
- return;
- }
-
- try {
- Application.SystemMessages ci = getSystemMessages();
- if (getRequestType(request) != RequestType.UIDL) {
- // 'plain' http req - e.g. browser reload;
- // just go ahead redirect the browser
- if (response instanceof ActionResponse) {
- ((ActionResponse) response).sendRedirect(ci
- .getCommunicationErrorURL());
- } else {
- // TODO What to do if we are e.g. rendering?
- }
- } else {
- // send uidl redirect
- criticalNotification(request, (ResourceResponse) response, ci
- .getCommunicationErrorCaption(), ci
- .getCommunicationErrorMessage(),
- INVALID_SECURITY_KEY_MSG, ci.getCommunicationErrorURL());
- /*
- * Invalidate session. Portal integration will fail otherwise
- * since the session is not created by the portal.
- */
- request.getPortletSession().invalidate();
- }
- } catch (SystemMessageException ee) {
- throw new PortletException(ee);
- }
- }
-
private void handleServiceException(PortletRequest request,
PortletResponse response, Application application, Throwable e)
throws IOException, PortletException {
+ // TODO Check that this error handler is working when running inside a
+ // portlet
+
// if this was an UIDL request, response UIDL back to client
if (getRequestType(request) == RequestType.UIDL) {
Application.SystemMessages ci = getSystemMessages();