diff options
79 files changed, 1199 insertions, 715 deletions
diff --git a/client/src/com/vaadin/client/VCaption.java b/client/src/com/vaadin/client/VCaption.java index 769428e16b..58e0b847f0 100644 --- a/client/src/com/vaadin/client/VCaption.java +++ b/client/src/com/vaadin/client/VCaption.java @@ -23,6 +23,7 @@ import com.google.gwt.user.client.ui.HTML; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.Icon; import com.vaadin.shared.AbstractFieldState; +import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ComponentState; public class VCaption extends HTML { @@ -119,7 +120,8 @@ public class VCaption extends HTML { } setStyleName(style); - boolean hasIcon = owner.getState().getIcon() != null; + boolean hasIcon = owner.getState().resources + .containsKey(ComponentConstants.ICON_RESOURCE); boolean showRequired = false; boolean showError = owner.getState().getErrorMessage() != null; if (owner.getState() instanceof AbstractFieldState) { @@ -143,7 +145,8 @@ public class VCaption extends HTML { // Icon forces the caption to be above the component placedAfterComponent = false; - icon.setUri(owner.getState().getIcon().getURL()); + icon.setUri(owner.getState().resources.get( + ComponentConstants.ICON_RESOURCE).getURL()); } else if (icon != null) { // Remove existing @@ -392,7 +395,7 @@ public class VCaption extends HTML { if (state.getCaption() != null) { return true; } - if (state.getIcon() != null) { + if (state.resources.containsKey(ComponentConstants.ICON_RESOURCE)) { return true; } if (state.getErrorMessage() != null) { diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java index 8a875fe363..18c13b4b7f 100644 --- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java @@ -39,6 +39,7 @@ import com.vaadin.client.metadata.Type; import com.vaadin.client.metadata.TypeData; import com.vaadin.client.ui.UI.UIConnector; import com.vaadin.client.ui.datefield.PopupDateFieldConnector; +import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ComponentState; import com.vaadin.shared.Connector; import com.vaadin.shared.ui.TabIndexState; @@ -248,9 +249,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector /* * (non-Javadoc) * - * @see - * com.vaadin.client.ComponentConnector#delegateCaptionHandling - * () + * @see com.vaadin.client.ComponentConnector#delegateCaptionHandling () */ @Override public boolean delegateCaptionHandling() { @@ -421,8 +420,7 @@ public abstract class AbstractComponentConnector extends AbstractConnector /* * (non-Javadoc) * - * @see - * com.vaadin.client.ComponentConnector#getTooltipInfo(com. + * @see com.vaadin.client.ComponentConnector#getTooltipInfo(com. * google.gwt.dom.client.Element) */ @Override @@ -430,4 +428,14 @@ public abstract class AbstractComponentConnector extends AbstractConnector return new TooltipInfo(getState().getDescription(), getState() .getErrorMessage()); } + + /** + * Gets the icon set for this component. + * + * @return the URL of the icon, or <code>null</code> if no icon has been + * defined. + */ + protected String getIcon() { + return getResourceUrl(ComponentConstants.ICON_RESOURCE); + } } diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java index a100f34130..989867d40e 100644 --- a/client/src/com/vaadin/client/ui/AbstractConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractConnector.java @@ -36,6 +36,7 @@ import com.vaadin.client.metadata.Type; import com.vaadin.client.metadata.TypeData; import com.vaadin.shared.communication.ClientRpc; import com.vaadin.shared.communication.SharedState; +import com.vaadin.shared.communication.URLReference; /** * An abstract implementation of Connector. @@ -344,4 +345,29 @@ public abstract class AbstractConnector implements ServerConnector, c.updateEnabledState(c.isEnabled()); } } + + /** + * Gets the URL for a resource that has been added by the server-side + * connector using + * {@link com.vaadin.terminal.AbstractClientConnector#setResource(String, com.vaadin.terminal.Resource)} + * with the same key. <code>null</code> is returned if no corresponding + * resource is found. + * <p> + * To get an event when a resource changes, you can use + * {@link #addStateChangeHandler(String, StateChangeHandler)} with + * <code>resources.[key]</code> as the property name. + * + * @param key + * a string identifying the resource. + * @return the resource URL as a string, or <code>null</code> if no + * corresponding resource is found. + */ + public String getResourceUrl(String key) { + URLReference urlReference = getState().resources.get(key); + if (urlReference == null) { + return null; + } else { + return urlReference.getURL(); + } + } } diff --git a/client/src/com/vaadin/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/client/ui/button/ButtonConnector.java index 103d0f5ae9..65acddb0a4 100644 --- a/client/src/com/vaadin/client/ui/button/ButtonConnector.java +++ b/client/src/com/vaadin/client/ui/button/ButtonConnector.java @@ -83,17 +83,17 @@ public class ButtonConnector extends AbstractComponentConnector implements } }); - addStateChangeHandler("icon", new StateChangeHandler() { + addStateChangeHandler("resources.icon", new StateChangeHandler() { @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { - if (getState().getIcon() != null) { + if (getIcon() != null) { if (getWidget().icon == null) { getWidget().icon = new Icon(getConnection()); getWidget().wrapper.insertBefore( getWidget().icon.getElement(), getWidget().captionElement); } - getWidget().icon.setUri(getState().getIcon().getURL()); + getWidget().icon.setUri(getIcon()); } else { if (getWidget().icon != null) { getWidget().wrapper.removeChild(getWidget().icon diff --git a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java index 8fb202ca9d..1a6547a85c 100644 --- a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java +++ b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java @@ -96,7 +96,7 @@ public class CheckBoxConnector extends AbstractFieldConnector implements getWidget().setEnabled(false); } - if (getState().getIcon() != null) { + if (getIcon() != null) { if (getWidget().icon == null) { getWidget().icon = new Icon(getConnection()); DOM.insertChild(getWidget().getElement(), @@ -104,7 +104,7 @@ public class CheckBoxConnector extends AbstractFieldConnector implements getWidget().icon.sinkEvents(VTooltip.TOOLTIP_EVENTS); getWidget().icon.sinkEvents(Event.ONCLICK); } - getWidget().icon.setUri(getState().getIcon().getURL()); + getWidget().icon.setUri(getIcon()); } else if (getWidget().icon != null) { // detach icon DOM.removeChild(getWidget().getElement(), diff --git a/client/src/com/vaadin/client/ui/embeddedbrowser/EmbeddedBrowserConnector.java b/client/src/com/vaadin/client/ui/embeddedbrowser/EmbeddedBrowserConnector.java index 6333cb2288..b0cd6d773d 100644 --- a/client/src/com/vaadin/client/ui/embeddedbrowser/EmbeddedBrowserConnector.java +++ b/client/src/com/vaadin/client/ui/embeddedbrowser/EmbeddedBrowserConnector.java @@ -2,6 +2,7 @@ package com.vaadin.client.ui.embeddedbrowser; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.shared.ui.AbstractEmbeddedState; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.embeddedbrowser.EmbeddedBrowserState; @@ -30,8 +31,7 @@ public class EmbeddedBrowserConnector extends AbstractComponentConnector { getWidget().setAlternateText(getState().getAlternateText()); getWidget().setSource( - getState().getSource() != null ? getState().getSource() - .getURL() : null); + getResourceUrl(AbstractEmbeddedState.SOURCE_RESOURCE)); getWidget().setName(getConnectorId()); } diff --git a/client/src/com/vaadin/client/ui/flash/FlashConnector.java b/client/src/com/vaadin/client/ui/flash/FlashConnector.java index e6158aa558..cc5423dd20 100644 --- a/client/src/com/vaadin/client/ui/flash/FlashConnector.java +++ b/client/src/com/vaadin/client/ui/flash/FlashConnector.java @@ -2,6 +2,7 @@ package com.vaadin.client.ui.flash; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.shared.ui.AbstractEmbeddedState; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.flash.FlashState; @@ -29,8 +30,7 @@ public class FlashConnector extends AbstractComponentConnector { super.onStateChanged(stateChangeEvent); getWidget().setSource( - getState().getSource() != null ? getState().getSource() - .getURL() : null); + getResourceUrl(AbstractEmbeddedState.SOURCE_RESOURCE)); getWidget().setArchive(getState().getArchive()); getWidget().setClassId(getState().getClassId()); getWidget().setCodebase(getState().getCodebase()); diff --git a/client/src/com/vaadin/client/ui/form/FormConnector.java b/client/src/com/vaadin/client/ui/form/FormConnector.java index 506813ef37..1b97bbc4cb 100644 --- a/client/src/com/vaadin/client/ui/form/FormConnector.java +++ b/client/src/com/vaadin/client/ui/form/FormConnector.java @@ -87,12 +87,12 @@ public class FormConnector extends AbstractComponentContainerConnector } else { getWidget().caption.setInnerText(""); } - if (getState().getIcon() != null) { + if (getIcon() != null) { if (getWidget().icon == null) { getWidget().icon = new Icon(client); getWidget().legend.insertFirst(getWidget().icon.getElement()); } - getWidget().icon.setUri(getState().getIcon().getURL()); + getWidget().icon.setUri(getIcon()); legendEmpty = false; } else { if (getWidget().icon != null) { diff --git a/client/src/com/vaadin/client/ui/formlayout/VFormLayout.java b/client/src/com/vaadin/client/ui/formlayout/VFormLayout.java index a178fe1cf5..890275dfe4 100644 --- a/client/src/com/vaadin/client/ui/formlayout/VFormLayout.java +++ b/client/src/com/vaadin/client/ui/formlayout/VFormLayout.java @@ -36,6 +36,7 @@ import com.vaadin.client.StyleConstants; import com.vaadin.client.VTooltip; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.Icon; +import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ComponentState; import com.vaadin.shared.ui.MarginInfo; @@ -253,13 +254,14 @@ public class VFormLayout extends SimplePanel { boolean isEmpty = true; - if (state.getIcon() != null) { + if (state.resources.containsKey(ComponentConstants.ICON_RESOURCE)) { if (icon == null) { icon = new Icon(owner.getConnection()); DOM.insertChild(getElement(), icon.getElement(), 0); } - icon.setUri(state.getIcon().getURL()); + icon.setUri(state.resources.get( + ComponentConstants.ICON_RESOURCE).getURL()); isEmpty = false; } else { if (icon != null) { diff --git a/client/src/com/vaadin/client/ui/image/ImageConnector.java b/client/src/com/vaadin/client/ui/image/ImageConnector.java index ce92ec48dd..7c3eb973d2 100644 --- a/client/src/com/vaadin/client/ui/image/ImageConnector.java +++ b/client/src/com/vaadin/client/ui/image/ImageConnector.java @@ -8,6 +8,7 @@ import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.ClickEventHandler; import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.AbstractEmbeddedState; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.image.ImageServerRpc; import com.vaadin.shared.ui.image.ImageState; @@ -48,8 +49,7 @@ public class ImageConnector extends AbstractComponentConnector { clickEventHandler.handleEventHandlerRegistration(); getWidget().setUrl( - getState().getSource() != null ? getState().getSource() - .getURL() : null); + getResourceUrl(AbstractEmbeddedState.SOURCE_RESOURCE)); getWidget().setAltText(getState().getAlternateText()); } diff --git a/client/src/com/vaadin/client/ui/link/LinkConnector.java b/client/src/com/vaadin/client/ui/link/LinkConnector.java index 2b19a943b3..26e0e78654 100644 --- a/client/src/com/vaadin/client/ui/link/LinkConnector.java +++ b/client/src/com/vaadin/client/ui/link/LinkConnector.java @@ -20,10 +20,13 @@ import com.google.gwt.user.client.DOM; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.Paintable; import com.vaadin.client.UIDL; +import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.Icon; import com.vaadin.shared.ui.BorderStyle; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.link.LinkConstants; import com.vaadin.ui.Link; @Connect(Link.class) @@ -31,6 +34,21 @@ public class LinkConnector extends AbstractComponentConnector implements Paintable { @Override + protected void init() { + super.init(); + addStateChangeHandler("resources." + LinkConstants.HREF_RESOURCE, + new StateChangeHandler() { + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + getWidget().src = getResourceUrl(LinkConstants.HREF_RESOURCE); + getWidget().anchor + .setAttribute("href", getWidget().src); + + } + }); + } + + @Override public boolean delegateCaptionHandling() { return false; } @@ -50,11 +68,6 @@ public class LinkConnector extends AbstractComponentConnector implements getWidget().target = uidl.getStringAttribute("name"); getWidget().anchor.setAttribute("target", getWidget().target); } - if (uidl.hasAttribute("src")) { - getWidget().src = client.translateVaadinUri(uidl - .getStringAttribute("src")); - getWidget().anchor.setAttribute("href", getWidget().src); - } if (uidl.hasAttribute("border")) { if ("none".equals(uidl.getStringAttribute("border"))) { @@ -88,13 +101,13 @@ public class LinkConnector extends AbstractComponentConnector implements "none"); } - if (getState().getIcon() != null) { + if (getIcon() != null) { if (getWidget().icon == null) { getWidget().icon = new Icon(client); getWidget().anchor.insertBefore(getWidget().icon.getElement(), getWidget().captionElement); } - getWidget().icon.setUri(getState().getIcon().getURL()); + getWidget().icon.setUri(getIcon()); } } diff --git a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java index 852be8e3cf..e821ae1422 100644 --- a/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java +++ b/client/src/com/vaadin/client/ui/nativebutton/NativeButtonConnector.java @@ -91,14 +91,14 @@ public class NativeButtonConnector extends AbstractComponentConnector implements getWidget().errorIndicatorElement = null; } - if (getState().getIcon() != null) { + if (getIcon() != null) { if (getWidget().icon == null) { getWidget().icon = new Icon(getConnection()); getWidget().getElement().insertBefore( getWidget().icon.getElement(), getWidget().captionElement); } - getWidget().icon.setUri(getState().getIcon().getURL()); + getWidget().icon.setUri(getIcon()); } else { if (getWidget().icon != null) { getWidget().getElement().removeChild( diff --git a/client/src/com/vaadin/client/ui/panel/PanelConnector.java b/client/src/com/vaadin/client/ui/panel/PanelConnector.java index 740a92d7cd..f991d295b4 100644 --- a/client/src/com/vaadin/client/ui/panel/PanelConnector.java +++ b/client/src/com/vaadin/client/ui/panel/PanelConnector.java @@ -140,8 +140,8 @@ public class PanelConnector extends AbstractComponentContainerConnector getWidget().client = client; getWidget().id = uidl.getId(); - if (getState().getIcon() != null) { - getWidget().setIconUri(getState().getIcon().getURL(), client); + if (getIcon() != null) { + getWidget().setIconUri(getIcon(), client); } else { getWidget().setIconUri(null, client); } diff --git a/client/src/com/vaadin/client/ui/video/VideoConnector.java b/client/src/com/vaadin/client/ui/video/VideoConnector.java index ff40c74c3d..d2d9804f66 100644 --- a/client/src/com/vaadin/client/ui/video/VideoConnector.java +++ b/client/src/com/vaadin/client/ui/video/VideoConnector.java @@ -17,8 +17,8 @@ package com.vaadin.client.ui.video; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.MediaBaseConnector; -import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.video.VideoConstants; import com.vaadin.shared.ui.video.VideoState; import com.vaadin.ui.Video; @@ -33,12 +33,7 @@ public class VideoConnector extends MediaBaseConnector { @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); - URLReference poster = getState().getPoster(); - if (poster != null) { - getWidget().setPoster(poster.getURL()); - } else { - getWidget().setPoster(null); - } + getWidget().setPoster(getResourceUrl(VideoConstants.POSTER_RESOURCE)); } @Override diff --git a/client/src/com/vaadin/client/ui/window/WindowConnector.java b/client/src/com/vaadin/client/ui/window/WindowConnector.java index f15866d602..b013bcaee0 100644 --- a/client/src/com/vaadin/client/ui/window/WindowConnector.java +++ b/client/src/com/vaadin/client/ui/window/WindowConnector.java @@ -115,8 +115,8 @@ SimpleManagedLayout, PostLayoutListener, MayScrollChildren { // Caption must be set before required header size is measured. If // the caption attribute is missing the caption should be cleared. String iconURL = null; - if (getState().getIcon() != null) { - iconURL = getState().getIcon().getURL(); + if (getIcon() != null) { + iconURL = getIcon(); } getWidget().setCaption(getState().getCaption(), iconURL); } diff --git a/server/src/com/vaadin/Application.java b/server/src/com/vaadin/Application.java index ef3c805bb1..7c80ce654a 100644 --- a/server/src/com/vaadin/Application.java +++ b/server/src/com/vaadin/Application.java @@ -30,7 +30,6 @@ import java.util.EventListener; import java.util.EventObject; import java.util.HashMap; import java.util.HashSet; -import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; @@ -53,7 +52,6 @@ import com.vaadin.data.util.converter.DefaultConverterFactory; import com.vaadin.event.EventRouter; import com.vaadin.server.AbstractApplicationServlet; import com.vaadin.server.AbstractErrorMessage; -import com.vaadin.server.ApplicationResource; import com.vaadin.server.BootstrapFragmentResponse; import com.vaadin.server.BootstrapListener; import com.vaadin.server.BootstrapPageResponse; @@ -62,14 +60,15 @@ import com.vaadin.server.ChangeVariablesErrorEvent; import com.vaadin.server.ClientConnector; import com.vaadin.server.CombinedRequest; import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.GlobalResourceHandler; import com.vaadin.server.RequestHandler; import com.vaadin.server.Terminal; import com.vaadin.server.UIProvider; import com.vaadin.server.VariableOwner; import com.vaadin.server.WebApplicationContext; import com.vaadin.server.WrappedRequest; -import com.vaadin.server.WrappedResponse; import com.vaadin.server.WrappedRequest.BrowserDetails; +import com.vaadin.server.WrappedResponse; import com.vaadin.service.ApplicationContext; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.tools.ReflectTools; @@ -123,8 +122,8 @@ import com.vaadin.ui.Window; * found out, the window itself is queried for a preferred theme. If the window * does not prefer a specific theme, the application containing the window is * queried. If neither the application prefers a theme, the default theme for - * the {@link com.vaadin.server.Terminal terminal} is used. The terminal - * always defines a default theme. + * the {@link com.vaadin.server.Terminal terminal} is used. The terminal always + * defines a default theme. * </p> * * @author Vaadin Ltd. @@ -458,10 +457,6 @@ public class Application implements Terminal.ErrorListener, Serializable { /** * Application resource mapping: key <-> resource. */ - private final Hashtable<ApplicationResource, String> resourceKeyMap = new Hashtable<ApplicationResource, String>(); - - private final Hashtable<String, ApplicationResource> keyResourceMap = new Hashtable<String, ApplicationResource>(); - private long lastResourceKeyNumber = 0; /** @@ -508,6 +503,8 @@ public class Application implements Terminal.ErrorListener, Serializable { private List<UIProvider> uiProviders = new LinkedList<UIProvider>(); + private GlobalResourceHandler globalResourceHandler; + /** * Gets the user of the application. * @@ -694,71 +691,6 @@ public class Application implements Terminal.ErrorListener, Serializable { } /** - * Adds new resource to the application. The resource can be accessed by the - * user of the application. - * - * @param resource - * the resource to add. - */ - public void addResource(ApplicationResource resource) { - - // Check if the resource is already mapped - if (resourceKeyMap.containsKey(resource)) { - return; - } - - // Generate key - final String key = String.valueOf(++lastResourceKeyNumber); - - // Add the resource to mappings - resourceKeyMap.put(resource, key); - keyResourceMap.put(key, resource); - } - - /** - * Removes the resource from the application. - * - * @param resource - * the resource to remove. - */ - public void removeResource(ApplicationResource resource) { - final Object key = resourceKeyMap.get(resource); - if (key != null) { - resourceKeyMap.remove(resource); - keyResourceMap.remove(key); - } - } - - /** - * Gets the relative uri of the resource. This method is intended to be - * called only be the terminal implementation. - * - * This method can only be called from within the processing of a UIDL - * request, not from a background thread. - * - * @param resource - * the resource to get relative location. - * @return the relative uri of the resource or null if called in a - * background thread - * - * @deprecated this method is intended to be used by the terminal only. It - * may be removed or moved in the future. - */ - @Deprecated - public String getRelativeLocation(ApplicationResource resource) { - - // Gets the key - final String key = resourceKeyMap.get(resource); - - // If the resource is not registered, return null - if (key == null) { - return null; - } - - return context.generateApplicationResourceURL(resource, key); - } - - /** * Gets the default locale for this application. * * By default this is the preferred locale of the user using the @@ -2066,20 +1998,6 @@ public class Application implements Terminal.ErrorListener, Serializable { } /** - * Find an application resource with a given key. - * - * @param key - * The key of the resource - * @return The application resource corresponding to the provided key, or - * <code>null</code> if no resource is registered for the key - * - * @since 7.0 - */ - public ApplicationResource getResource(String key) { - return keyResourceMap.get(key); - } - - /** * Thread local for keeping track of currently used application instance * * @since 7.0 @@ -2510,4 +2428,30 @@ public class Application implements Terminal.ErrorListener, Serializable { } return true; } + + /** + * Gets this application's global resource handler that takes care of + * serving connector resources that are not served by any single connector + * because e.g. because they are served with strong caching or because of + * legacy reasons. + * + * @param createOnDemand + * <code>true</code> if a resource handler should be initialized + * if there is no handler associated with this application. + * </code>false</code> if </code>null</code> should be returned + * if there is no registered handler. + * @return this application's global resource handler, or <code>null</code> + * if there is no handler and the createOnDemand parameter is + * <code>false</code>. + * + * @since 7.0.0 + */ + public GlobalResourceHandler getGlobalResourceHandler(boolean createOnDemand) { + if (globalResourceHandler == null && createOnDemand) { + globalResourceHandler = new GlobalResourceHandler(); + addRequestHandler(globalResourceHandler); + } + + return globalResourceHandler; + } } diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 745ccf679c..fa127fa2aa 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -15,6 +15,7 @@ */ package com.vaadin.server; +import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; @@ -367,8 +368,8 @@ public abstract class AbstractClientConnector implements ClientConnector { /** * Finds a UI ancestor of this connector. <code>null</code> is returned if - * no UI ancestor is found (typically because the connector is not - * attached to a proper hierarchy). + * no UI ancestor is found (typically because the connector is not attached + * to a proper hierarchy). * * @return the UI ancestor of this connector, or <code>null</code> if none * is found. @@ -561,4 +562,59 @@ public abstract class AbstractClientConnector implements ClientConnector { public void beforeClientResponse(boolean initial) { // Do nothing by default } + + @Override + public boolean handleConnectorRequest(WrappedRequest request, + WrappedResponse response, String path) throws IOException { + String[] parts = path.split("/", 2); + String key = parts[0]; + + ConnectorResource resource = (ConnectorResource) getResource(key); + if (resource != null) { + DownloadStream stream = resource.getStream(); + stream.writeTo(response); + return true; + } else { + return false; + } + } + + /** + * Gets a resource defined using {@link #setResource(String, Resource)} with + * the corresponding key. + * + * @param key + * the string identifier of the resource + * @return a resource, or <code>null</code> if there's no resource + * associated with the given key + * + * @see #setResource(String, Resource) + */ + protected Resource getResource(String key) { + return ResourceReference.getResource(getState().resources.get(key)); + } + + /** + * Registers a resource with this connector using the given key. This will + * make the URL for retrieving the resource available to the client-side + * connector using + * {@link com.vaadin.terminal.gwt.client.ui.AbstractConnector#getResourceUrl(String)} + * with the same key. + * + * @param key + * the string key to associate the resource with + * @param resource + * the resource to set, or <code>null</code> to clear a previous + * association. + */ + protected void setResource(String key, Resource resource) { + ResourceReference resourceReference = ResourceReference.create( + resource, this, key); + + if (resourceReference == null) { + getState().resources.remove(key); + } else { + getState().resources.put(key, resourceReference); + } + } } diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java index 2d60c6e587..2655ee9a00 100644 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/server/AbstractCommunicationManager.java @@ -103,10 +103,10 @@ public abstract class AbstractCommunicationManager implements Serializable { private static final String DASHDASH = "--"; - private static final RequestHandler APP_RESOURCE_HANDLER = new ApplicationResourceHandler(); - private static final RequestHandler UNSUPPORTED_BROWSER_HANDLER = new UnsupportedBrowserHandler(); + private static final RequestHandler CONNECTOR_RESOURCE_HANDLER = new ConnectorResourceHandler(); + /** * TODO Document me! * @@ -178,8 +178,8 @@ public abstract class AbstractCommunicationManager implements Serializable { public AbstractCommunicationManager(Application application) { this.application = application; application.addRequestHandler(getBootstrapHandler()); - application.addRequestHandler(APP_RESOURCE_HANDLER); application.addRequestHandler(UNSUPPORTED_BROWSER_HANDLER); + application.addRequestHandler(CONNECTOR_RESOURCE_HANDLER); requireLocale(application.getLocale().toString()); } diff --git a/server/src/com/vaadin/server/AbstractWebApplicationContext.java b/server/src/com/vaadin/server/AbstractWebApplicationContext.java index 78cf8fdab8..cf983f4c80 100644 --- a/server/src/com/vaadin/server/AbstractWebApplicationContext.java +++ b/server/src/com/vaadin/server/AbstractWebApplicationContext.java @@ -18,9 +18,6 @@ package com.vaadin.server; import java.io.PrintWriter; import java.io.Serializable; import java.io.StringWriter; -import java.io.UnsupportedEncodingException; -import java.net.URL; -import java.net.URLEncoder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -35,7 +32,6 @@ import javax.servlet.http.HttpSessionBindingListener; import com.vaadin.Application; import com.vaadin.service.ApplicationContext; -import com.vaadin.shared.ApplicationConstants; /** * Base class for web application contexts (including portlet contexts) that @@ -186,66 +182,6 @@ public abstract class AbstractWebApplicationContext implements applicationToAjaxAppMgrMap.remove(application); } - @Override - public String generateApplicationResourceURL(ApplicationResource resource, - String mapKey) { - - final String filename = resource.getFilename(); - if (filename == null) { - return ApplicationConstants.APP_PROTOCOL_PREFIX - + ApplicationConstants.APP_REQUEST_PATH + mapKey + "/"; - } else { - // #7738 At least Tomcat and JBoss refuses requests containing - // encoded slashes or backslashes in URLs. Application resource URLs - // should really be passed in another way than as part of the path - // in the future. - String encodedFileName = urlEncode(filename).replace("%2F", "/") - .replace("%5C", "\\"); - return ApplicationConstants.APP_PROTOCOL_PREFIX - + ApplicationConstants.APP_REQUEST_PATH + mapKey + "/" - + encodedFileName; - } - - } - - static String urlEncode(String filename) { - try { - return URLEncoder.encode(filename, "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException( - "UTF-8 charset not available (\"this should never happen\")", - e); - } - } - - @Override - public boolean isApplicationResourceURL(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")); - } - - @Override - public String getURLKey(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); - } - /** * @return The total time spent servicing requests in this session. */ diff --git a/server/src/com/vaadin/server/ApplicationResource.java b/server/src/com/vaadin/server/ApplicationResource.java deleted file mode 100644 index b18886ed55..0000000000 --- a/server/src/com/vaadin/server/ApplicationResource.java +++ /dev/null @@ -1,85 +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.io.Serializable; - -import com.vaadin.Application; - -/** - * This interface must be implemented by classes wishing to provide Application - * resources. - * <p> - * <code>ApplicationResource</code> are a set of named resources (pictures, - * sounds, etc) associated with some specific application. Having named - * application resources provides a convenient method for having inter-theme - * common resources for an application. - * </p> - * - * @author Vaadin Ltd. - * @since 3.0 - */ -public interface ApplicationResource extends Resource, Serializable { - - /** - * Default cache time. - */ - public static final long DEFAULT_CACHETIME = 1000 * 60 * 60 * 24; - - /** - * Gets resource as stream. - */ - public DownloadStream getStream(); - - /** - * Gets the application of the resource. - */ - public Application getApplication(); - - /** - * Gets the virtual filename for this resource. - * - * @return the file name associated to this resource. - */ - public String getFilename(); - - /** - * Gets the length of cache expiration time. - * - * <p> - * This gives the adapter the possibility cache streams sent to the client. - * The caching may be made in adapter or at the client if the client - * supports caching. Default is <code>DEFAULT_CACHETIME</code>. - * </p> - * - * @return Cache time in milliseconds - */ - public long getCacheTime(); - - /** - * Gets the size of the download buffer used for this resource. - * - * <p> - * If the buffer size is 0, the buffer size is decided by the terminal - * adapter. The default value is 0. - * </p> - * - * @return int the size of the buffer in bytes. - */ - public int getBufferSize(); - -} diff --git a/server/src/com/vaadin/server/ApplicationResourceHandler.java b/server/src/com/vaadin/server/ApplicationResourceHandler.java deleted file mode 100644 index 1cad5efcfd..0000000000 --- a/server/src/com/vaadin/server/ApplicationResourceHandler.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.io.IOException; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.servlet.http.HttpServletResponse; - -import com.vaadin.Application; - -public class ApplicationResourceHandler implements RequestHandler { - private static final Pattern APP_RESOURCE_PATTERN = Pattern - .compile("^/?APP/(\\d+)/.*"); - - @Override - public boolean handleRequest(Application application, - WrappedRequest request, WrappedResponse response) - throws IOException { - // Check for application resources - String requestPath = request.getRequestPathInfo(); - if (requestPath == null) { - return false; - } - Matcher resourceMatcher = APP_RESOURCE_PATTERN.matcher(requestPath); - - if (resourceMatcher.matches()) { - ApplicationResource resource = application - .getResource(resourceMatcher.group(1)); - if (resource != null) { - DownloadStream stream = resource.getStream(); - if (stream != null) { - stream.setCacheTime(resource.getCacheTime()); - stream.writeTo(response); - return true; - } - } - // We get here if the url looks like an application resource but no - // resource can be served - response.sendError(HttpServletResponse.SC_NOT_FOUND, - request.getRequestPathInfo() + " can not be found"); - return true; - } - - return false; - } -} diff --git a/server/src/com/vaadin/server/ClassResource.java b/server/src/com/vaadin/server/ClassResource.java index 4b4837faa3..2f05115fd4 100644 --- a/server/src/com/vaadin/server/ClassResource.java +++ b/server/src/com/vaadin/server/ClassResource.java @@ -20,6 +20,8 @@ import java.io.Serializable; import com.vaadin.Application; import com.vaadin.service.FileTypeResolver; +import com.vaadin.ui.UI; +import com.vaadin.ui.UI.LegacyWindow; /** * <code>ClassResource</code> is a named resource accessed with the class @@ -33,7 +35,7 @@ import com.vaadin.service.FileTypeResolver; * @since 3.0 */ @SuppressWarnings("serial") -public class ClassResource implements ApplicationResource, Serializable { +public class ClassResource implements ConnectorResource, Serializable { /** * Default buffer size for this stream resource. @@ -43,10 +45,10 @@ public class ClassResource implements ApplicationResource, Serializable { /** * Default cache time for this stream resource. */ - private long cacheTime = DEFAULT_CACHETIME; + private long cacheTime = DownloadStream.DEFAULT_CACHETIME; /** - * Associated class used for indetifying the source of the resource. + * Associated class used for identifying the source of the resource. */ private final Class<?> associatedClass; @@ -56,21 +58,15 @@ public class ClassResource implements ApplicationResource, Serializable { private final String resourceName; /** - * Application used for serving the class. - */ - private final Application application; - - /** * Creates a new application resource instance. The resource id is relative - * to the location of the application class. + * to the location of the UI of the component using this resource (or the + * Application if using LegacyWindow). * * @param resourceName * the Unique identifier of the resource within the application. - * @param application - * the application this resource will be added to. */ - public ClassResource(String resourceName, Application application) { - this(application.getClass(), resourceName, application); + public ClassResource(String resourceName) { + this(null, resourceName); } /** @@ -80,18 +76,13 @@ public class ClassResource implements ApplicationResource, Serializable { * the class of the which the resource is associated. * @param resourceName * the Unique identifier of the resource within the application. - * @param application - * the application this resource will be added to. */ - public ClassResource(Class<?> associatedClass, String resourceName, - Application application) { + public ClassResource(Class<?> associatedClass, String resourceName) { this.associatedClass = associatedClass; this.resourceName = resourceName; - this.application = application; - if (resourceName == null || associatedClass == null) { + if (resourceName == null) { throw new NullPointerException(); } - application.addResource(this); } /** @@ -104,50 +95,43 @@ public class ClassResource implements ApplicationResource, Serializable { return FileTypeResolver.getMIMEType(resourceName); } - /** - * Gets the application of this resource. - * - * @see com.vaadin.server.ApplicationResource#getApplication() - */ - @Override - public Application getApplication() { - return application; - } - - /** - * Gets the virtual filename for this resource. - * - * @return the file name associated to this resource. - * @see com.vaadin.server.ApplicationResource#getFilename() - */ @Override public String getFilename() { - int index = 0; - int next = 0; - while ((next = resourceName.indexOf('/', index)) > 0 - && next + 1 < resourceName.length()) { - index = next + 1; - } - return resourceName.substring(index); + String[] parts = resourceName.split("/"); + return parts[parts.length - 1]; } - /** - * Gets resource as stream. - * - * @see com.vaadin.server.ApplicationResource#getStream() - */ @Override public DownloadStream getStream() { - final DownloadStream ds = new DownloadStream( - associatedClass.getResourceAsStream(resourceName), - getMIMEType(), getFilename()); + final DownloadStream ds = new DownloadStream(getAssociatedClass() + .getResourceAsStream(resourceName), getMIMEType(), + getFilename()); ds.setBufferSize(getBufferSize()); - ds.setCacheTime(cacheTime); + ds.setCacheTime(getCacheTime()); return ds; } - /* documented in superclass */ - @Override + protected Class<?> getAssociatedClass() { + if (associatedClass == null) { + Class<? extends UI> associatedClass = UI.getCurrent().getClass(); + if (associatedClass == LegacyWindow.class) { + return Application.getCurrent().getClass(); + } + return associatedClass; + } + return associatedClass; + } + + /** + * Gets the size of the download buffer used for this resource. + * + * <p> + * If the buffer size is 0, the buffer size is decided by the terminal + * adapter. The default value is 0. + * </p> + * + * @return the size of the buffer in bytes. + */ public int getBufferSize() { return bufferSize; } @@ -157,13 +141,24 @@ public class ClassResource implements ApplicationResource, Serializable { * * @param bufferSize * the size of the buffer in bytes. + * + * @see #getBufferSize() */ public void setBufferSize(int bufferSize) { this.bufferSize = bufferSize; } - /* documented in superclass */ - @Override + /** + * Gets the length of cache expiration time. + * + * <p> + * This gives the adapter the possibility cache streams sent to the client. + * The caching may be made in adapter or at the client if the client + * supports caching. Default is {@link DownloadStream#DEFAULT_CACHETIME}. + * </p> + * + * @return Cache time in milliseconds + */ public long getCacheTime() { return cacheTime; } @@ -174,7 +169,7 @@ public class ClassResource implements ApplicationResource, Serializable { * <p> * This gives the adapter the possibility cache streams sent to the client. * The caching may be made in adapter or at the client if the client - * supports caching. Zero or negavive value disbales the caching of this + * supports caching. Zero or negative value disables the caching of this * stream. * </p> * diff --git a/server/src/com/vaadin/server/ClientConnector.java b/server/src/com/vaadin/server/ClientConnector.java index ce392ce226..3a340c2d7d 100644 --- a/server/src/com/vaadin/server/ClientConnector.java +++ b/server/src/com/vaadin/server/ClientConnector.java @@ -15,6 +15,7 @@ */ package com.vaadin.server; +import java.io.IOException; import java.util.Collection; import java.util.List; @@ -211,4 +212,30 @@ public interface ClientConnector extends Connector, RpcTarget { * if the state can not be encoded */ public JSONObject encodeState() throws JSONException; + + /** + * Handle a request directed to this connector. This can be used by + * connectors to dynamically generate a response and it is also used + * internally when serving {@link ConnectorResource}s. + * <p> + * Requests to <code>/APP/connector/[ui id]/[connector id]/</code> are + * routed to this method with the remaining part of the requested path + * available in the path parameter. + * <p> + * {@link DynamicConnectorResource} can be used to easily make an + * appropriate URL available to the client-side code. + * + * @param request + * the request that should be handled + * @param response + * the response object to which the response should be written + * @param path + * the requested relative path + * @return <code>true</code> if the request has been handled, + * <code>false</code> if no response has been written. + * @throws IOException + * if there is a problem generating a response. + */ + public boolean handleConnectorRequest(WrappedRequest request, + WrappedResponse response, String path) throws IOException; } diff --git a/server/src/com/vaadin/server/ConnectorResource.java b/server/src/com/vaadin/server/ConnectorResource.java new file mode 100644 index 0000000000..7f30de4bbe --- /dev/null +++ b/server/src/com/vaadin/server/ConnectorResource.java @@ -0,0 +1,43 @@ +/* + * 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; + + +/** + * A resource that is served through the Connector that is using the resource. + * + * @see AbstractClientConnector#setResource(String, Resource) + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public interface ConnectorResource extends Resource { + public static final String CONNECTOR_REQUEST_PATH = "connector/"; + + /** + * Gets resource as stream. + */ + public DownloadStream getStream(); + + /** + * Gets the virtual filename for this resource. + * + * @return the file name associated to this resource. + */ + public String getFilename(); +} diff --git a/server/src/com/vaadin/server/ConnectorResourceHandler.java b/server/src/com/vaadin/server/ConnectorResourceHandler.java new file mode 100644 index 0000000000..b988510b8e --- /dev/null +++ b/server/src/com/vaadin/server/ConnectorResourceHandler.java @@ -0,0 +1,81 @@ +package com.vaadin.server; + +import java.io.IOException; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletResponse; + +import com.vaadin.Application; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.ui.UI; + +public class ConnectorResourceHandler implements RequestHandler { + // APP/connector/[uiid]/[cid]/[filename.xyz] + private static final Pattern CONNECTOR_RESOURCE_PATTERN = Pattern + .compile("^/?" + ApplicationConstants.APP_REQUEST_PATH + + ConnectorResource.CONNECTOR_REQUEST_PATH + + "(\\d+)/(\\d+)/(.*)"); + + private static Logger getLogger() { + return Logger.getLogger(ConnectorResourceHandler.class.getName()); + + } + + @Override + public boolean handleRequest(Application application, + WrappedRequest request, WrappedResponse response) + throws IOException { + String requestPath = request.getRequestPathInfo(); + if (requestPath == null) { + return false; + } + Matcher matcher = CONNECTOR_RESOURCE_PATTERN.matcher(requestPath); + if (matcher.matches()) { + String uiId = matcher.group(1); + String cid = matcher.group(2); + String key = matcher.group(3); + UI ui = application.getUIById(Integer.parseInt(uiId)); + if (ui == null) { + return error(request, response, + "Ignoring connector request for no-existent root " + + uiId); + } + + UI.setCurrent(ui); + Application.setCurrent(ui.getApplication()); + + ClientConnector connector = ui.getConnectorTracker().getConnector( + cid); + if (connector == null) { + return error(request, response, + "Ignoring connector request for no-existent connector " + + cid + " in root " + uiId); + } + + if (!connector.handleConnectorRequest(request, response, key)) { + return error(request, response, connector.getClass() + .getSimpleName() + + " (" + + connector.getConnectorId() + + ") did not handle connector request for " + key); + } + + return true; + } else { + return false; + } + } + + private static boolean error(WrappedRequest request, + WrappedResponse response, String logMessage) throws IOException { + getLogger().log(Level.WARNING, logMessage); + response.sendError(HttpServletResponse.SC_NOT_FOUND, + request.getRequestPathInfo() + " can not be found"); + + // Request handled (though not in a nice way) + return true; + } +} diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java index 29825496b5..3e7de5c9a2 100644 --- a/server/src/com/vaadin/server/DragAndDropService.java +++ b/server/src/com/vaadin/server/DragAndDropService.java @@ -15,6 +15,7 @@ */ package com.vaadin.server; +import java.io.IOException; import java.io.PrintWriter; import java.util.Collection; import java.util.Collections; @@ -338,4 +339,10 @@ public class DragAndDropService implements VariableOwner, ClientConnector { // TODO Auto-generated method stub return null; } + + @Override + public boolean handleConnectorRequest(WrappedRequest request, + WrappedResponse response, String path) throws IOException { + return false; + } } diff --git a/server/src/com/vaadin/server/DynamicConnectorResource.java b/server/src/com/vaadin/server/DynamicConnectorResource.java new file mode 100644 index 0000000000..8269f261f7 --- /dev/null +++ b/server/src/com/vaadin/server/DynamicConnectorResource.java @@ -0,0 +1,95 @@ +/* + * 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.Collections; +import java.util.Map; + +import com.vaadin.service.FileTypeResolver; + +/** + * A resource that is served by calling + * {@link ClientConnector#handleConnectorRequest(WrappedRequest, WrappedResponse, String)} + * with appropriate parameters. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class DynamicConnectorResource implements Resource { + + private final ClientConnector connector; + private final String path; + private final Map<String, String> parameters; + + /** + * Creates a DynamicConnectorResoruce for the given connector that will be + * served by calling + * {@link ClientConnector#handleConnectorRequest(WrappedRequest, WrappedResponse, String)} + * with the given path. + * + * @param connector + * the connector that should serve the resource + * @param path + * the relative path of the request + */ + public DynamicConnectorResource(ClientConnector connector, String path) { + this(connector, path, null); + } + + /** + * Creates a DynamicConnectorResoruce for the given connector that will be + * served by calling + * {@link ClientConnector#handleConnectorRequest(WrappedRequest, WrappedResponse, String)} + * with the given path and the given request parameters. + * + * @param connector + * the connector that should serve the resource + * @param path + * the relative path of the request + * @param parameters + * the parameters that should be present in the request + */ + public DynamicConnectorResource(ClientConnector connector, String path, + Map<String, String> parameters) { + this.connector = connector; + this.path = path; + this.parameters = parameters; + } + + @Override + public String getMIMEType() { + return FileTypeResolver.getMIMEType(path); + } + + public String getPath() { + return path; + } + + public ClientConnector getConnector() { + return connector; + } + + public Map<String, String> getParameters() { + if (parameters == null) { + return Collections.emptyMap(); + } else { + return Collections.unmodifiableMap(parameters); + } + } + +} diff --git a/server/src/com/vaadin/server/FileResource.java b/server/src/com/vaadin/server/FileResource.java index 7b3f338b4f..fbf353362e 100644 --- a/server/src/com/vaadin/server/FileResource.java +++ b/server/src/com/vaadin/server/FileResource.java @@ -34,7 +34,7 @@ import com.vaadin.service.FileTypeResolver; * @since 3.0 */ @SuppressWarnings("serial") -public class FileResource implements ApplicationResource { +public class FileResource implements ConnectorResource { /** * Default buffer size for this stream resource. @@ -47,11 +47,6 @@ public class FileResource implements ApplicationResource { private File sourceFile; /** - * Application. - */ - private final Application application; - - /** * Default cache time for this stream resource. */ private long cacheTime = DownloadStream.DEFAULT_CACHETIME; @@ -59,18 +54,14 @@ public class FileResource implements ApplicationResource { /** * Creates a new file resource for providing given file for client * terminals. + * + * @param sourceFile + * the file that should be served. */ - public FileResource(File sourceFile, Application application) { - this.application = application; + public FileResource(File sourceFile) { setSourceFile(sourceFile); - application.addResource(this); } - /** - * Gets the resource as stream. - * - * @see com.vaadin.server.ApplicationResource#getStream() - */ @Override public DownloadStream getStream() { try { @@ -83,14 +74,15 @@ public class FileResource implements ApplicationResource { return ds; } catch (final FileNotFoundException e) { // Log the exception using the application error handler - getApplication().getErrorHandler().terminalError(new ErrorEvent() { + Application.getCurrent().getErrorHandler() + .terminalError(new ErrorEvent() { - @Override - public Throwable getThrowable() { - return e; - } + @Override + public Throwable getThrowable() { + return e; + } - }); + }); return null; } @@ -111,29 +103,15 @@ public class FileResource implements ApplicationResource { * @param sourceFile * the source file to set. */ - public void setSourceFile(File sourceFile) { + private void setSourceFile(File sourceFile) { this.sourceFile = sourceFile; } - /** - * @see com.vaadin.server.ApplicationResource#getApplication() - */ - @Override - public Application getApplication() { - return application; - } - - /** - * @see com.vaadin.server.ApplicationResource#getFilename() - */ @Override public String getFilename() { return sourceFile.getName(); } - /** - * @see com.vaadin.server.Resource#getMIMEType() - */ @Override public String getMIMEType() { return FileTypeResolver.getMIMEType(sourceFile); @@ -147,7 +125,6 @@ public class FileResource implements ApplicationResource { * * @return Cache time in milliseconds. */ - @Override public long getCacheTime() { return cacheTime; } @@ -165,8 +142,16 @@ public class FileResource implements ApplicationResource { this.cacheTime = cacheTime; } - /* documented in superclass */ - @Override + /** + * Gets the size of the download buffer used for this resource. + * + * <p> + * If the buffer size is 0, the buffer size is decided by the terminal + * adapter. The default value is 0. + * </p> + * + * @return the size of the buffer in bytes. + */ public int getBufferSize() { return bufferSize; } diff --git a/server/src/com/vaadin/server/GlobalResourceHandler.java b/server/src/com/vaadin/server/GlobalResourceHandler.java new file mode 100644 index 0000000000..7038d51251 --- /dev/null +++ b/server/src/com/vaadin/server/GlobalResourceHandler.java @@ -0,0 +1,239 @@ +/* + * 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.io.IOException; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.servlet.http.HttpServletResponse; + +import com.vaadin.Application; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.ui.UI; + +/** + * A {@link RequestHandler} that takes care of {@link ConnectorResource}s that + * should not be served by the connector. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class GlobalResourceHandler implements RequestHandler { + private static final String LEGACY_TYPE = "legacy"; + + private static final String RESOURCE_REQUEST_PATH = "global/"; + + /** + * Used to detect when a resource is no longer used by any connector. + */ + private final Map<Resource, Set<ClientConnector>> resourceUsers = new HashMap<Resource, Set<ClientConnector>>(); + /** + * Used to find the resources that might not be needed any more when a + * connector is unregistered. + */ + private final Map<ClientConnector, Set<Resource>> usedResources = new HashMap<ClientConnector, Set<Resource>>(); + + private final Map<ConnectorResource, String> legacyResourceKeys = new HashMap<ConnectorResource, String>(); + private final Map<String, ConnectorResource> legacyResources = new HashMap<String, ConnectorResource>(); + private int nextLegacyId = 0; + + // APP/global/[uiid]/[type]/[id] + private final Matcher matcher = Pattern.compile( + "^/?" + ApplicationConstants.APP_REQUEST_PATH + + RESOURCE_REQUEST_PATH + "(\\d+)/(([^/]+)(/.*))").matcher( + ""); + + @Override + public boolean handleRequest(Application application, + WrappedRequest request, WrappedResponse response) + throws IOException { + String pathInfo = request.getRequestPathInfo(); + if (pathInfo == null) { + return false; + } + + matcher.reset(pathInfo); + if (!matcher.matches()) { + return false; + } + + String uiid = matcher.group(1); + String type = matcher.group(3); + String key = matcher.group(2); + + // Allow GCing pathInfo string + matcher.reset(); + + if (key == null) { + return error(request, response, pathInfo + + " is not a valid global resource path"); + } + + UI ui = application.getUIById(Integer.parseInt(uiid)); + if (ui == null) { + return error(request, response, "No UI found for id " + uiid); + } + UI.setCurrent(ui); + + ConnectorResource resource; + if (LEGACY_TYPE.equals(type)) { + resource = legacyResources.get(key); + } else { + return error(request, response, "Unknown global resource type " + + type + " in requested path " + pathInfo); + } + + if (resource == null) { + return error(request, response, "Global resource " + key + + " not found"); + } + + DownloadStream stream = resource.getStream(); + if (stream == null) { + return error(request, response, "Resource " + resource + + " didn't produce any stream."); + } + + stream.writeTo(response); + return true; + } + + /** + * Registers a resource to be served with a global URL. + * <p> + * A {@link ConnectorResource} registered for a {@link Vaadin6Component} + * will be set to be served with a global URL. Other resource types will be + * ignored and thus not served by this handler. + * + * @param resource + * the resource to register + * @param ownerConnector + * the connector to which the resource belongs + */ + public void register(Resource resource, ClientConnector ownerConnector) { + if (resource instanceof ConnectorResource) { + if (!(ownerConnector instanceof LegacyComponent)) { + throw new IllegalArgumentException( + "A normal ConnectorResource can only be registered for legacy components."); + } + ConnectorResource connectorResource = (ConnectorResource) resource; + if (!legacyResourceKeys.containsKey(resource)) { + String uri = LEGACY_TYPE + '/' + + Integer.toString(nextLegacyId++); + String filename = connectorResource.getFilename(); + if (filename != null && !filename.isEmpty()) { + uri += '/' + filename; + } + legacyResourceKeys.put(connectorResource, uri); + legacyResources.put(uri, connectorResource); + registerResourceUsage(connectorResource, ownerConnector); + } + } + } + + private void unregisterResource(Resource resource) { + String oldUri = legacyResourceKeys.remove(resource); + if (oldUri != null) { + legacyResources.remove(oldUri); + } + } + + private void registerResourceUsage(Resource resource, + ClientConnector connector) { + ensureInSet(resourceUsers, resource, connector); + ensureInSet(usedResources, connector, resource); + } + + private <K, V> void ensureInSet(Map<K, Set<V>> map, K key, V value) { + Set<V> set = map.get(key); + if (set == null) { + set = new HashSet<V>(); + map.put(key, set); + } + set.add(value); + } + + /** + * Gets a global URI for a resource if it's registered with this handler. + * + * @param connector + * the connector for which the uri should be generated. + * @param resource + * the resource for which the uri should be generated. + * @return an URI string, or <code>null</code> if the resource is not + * registered. + */ + public String getUri(ClientConnector connector, ConnectorResource resource) { + // app://APP/global/[ui]/[type]/[id] + String uri = legacyResourceKeys.get(resource); + if (uri != null && !uri.isEmpty()) { + return ApplicationConstants.APP_PROTOCOL_PREFIX + + ApplicationConstants.APP_REQUEST_PATH + + RESOURCE_REQUEST_PATH + connector.getUI().getUIId() + '/' + + uri; + } else { + return null; + } + } + + /** + * Notifies this handler that resources registered for the given connector + * can be released. + * + * @param connector + * the connector for which any registered resources can be + * released. + */ + public void unregisterConnector(ClientConnector connector) { + Set<Resource> set = usedResources.remove(connector); + if (set == null) { + return; + } + + for (Resource resource : set) { + Set<ClientConnector> users = resourceUsers.get(resource); + users.remove(connector); + if (users.isEmpty()) { + resourceUsers.remove(resource); + unregisterResource(resource); + } + } + } + + private static Logger getLogger() { + return Logger.getLogger(GlobalResourceHandler.class.getName()); + } + + private static boolean error(WrappedRequest request, + WrappedResponse response, String logMessage) throws IOException { + getLogger().log(Level.WARNING, logMessage); + response.sendError(HttpServletResponse.SC_NOT_FOUND, + request.getRequestPathInfo() + " can not be found"); + + // Request handled (though not in a nice way) + return true; + } + +} diff --git a/server/src/com/vaadin/server/JsonPaintTarget.java b/server/src/com/vaadin/server/JsonPaintTarget.java index 96d7cec82e..f776e38a1f 100644 --- a/server/src/com/vaadin/server/JsonPaintTarget.java +++ b/server/src/com/vaadin/server/JsonPaintTarget.java @@ -344,7 +344,12 @@ public class JsonPaintTarget implements PaintTarget { if (value == null) { throw new NullPointerException(); } - ResourceReference reference = ResourceReference.create(value); + ClientConnector ownerConnector = openPaintables.peek(); + ownerConnector.getUI().getApplication().getGlobalResourceHandler(true) + .register(value, ownerConnector); + + ResourceReference reference = ResourceReference.create(value, + ownerConnector, name); addAttribute(name, reference.getURL()); } diff --git a/server/src/com/vaadin/server/ResourceReference.java b/server/src/com/vaadin/server/ResourceReference.java index f2af92aa73..098fb6c3e4 100644 --- a/server/src/com/vaadin/server/ResourceReference.java +++ b/server/src/com/vaadin/server/ResourceReference.java @@ -15,15 +15,25 @@ */ package com.vaadin.server; -import com.vaadin.Application; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map.Entry; +import java.util.Set; + +import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.communication.URLReference; public class ResourceReference extends URLReference { - private Resource resource; + private final Resource resource; + private final ClientConnector connector; + private final String key; - public ResourceReference(Resource resource) { + public ResourceReference(Resource resource, ClientConnector connector, + String key) { this.resource = resource; + this.connector = connector; + this.key = key; } public Resource getResource() { @@ -34,16 +44,49 @@ public class ResourceReference extends URLReference { public String getURL() { if (resource instanceof ExternalResource) { return ((ExternalResource) resource).getURL(); - } else if (resource instanceof ApplicationResource) { - final ApplicationResource r = (ApplicationResource) resource; - final Application a = r.getApplication(); - if (a == null) { - throw new RuntimeException( - "An ApplicationResource (" - + r.getClass().getName() - + " must be attached to an application when it is sent to the client."); + } else if (resource instanceof DynamicConnectorResource) { + DynamicConnectorResource dcr = (DynamicConnectorResource) resource; + + String filename = dcr.getPath(); + StringBuilder builder = new StringBuilder(getConnectorResourceBase( + filename, dcr.getConnector())); + + Set<Entry<String, String>> entrySet = dcr.getParameters() + .entrySet(); + boolean first = true; + for (Entry<String, String> entry : entrySet) { + String key = entry.getKey(); + String value = entry.getValue(); + if (first) { + builder.append('?'); + first = false; + } else { + builder.append('&'); + } + // TODO URL encode!!! + builder.append(key).append('=').append(value); + } + return builder.toString(); + } else if (resource instanceof ConnectorResource) { + ConnectorResource connectorResource = (ConnectorResource) resource; + + GlobalResourceHandler globalResourceHandler = connector.getUI() + .getApplication().getGlobalResourceHandler(false); + if (globalResourceHandler != null) { + String uri = globalResourceHandler.getUri(connector, + connectorResource); + if (uri != null && !uri.isEmpty()) { + return uri; + } + } + + // app://APP/connector/[uiid]/[cid]/[key]/[filename] + String prefix = key; + String filename = connectorResource.getFilename(); + if (filename != null && !filename.isEmpty()) { + prefix += '/' + filename; } - final String uri = a.getRelativeLocation(r); + String uri = getConnectorResourceBase(prefix, connector); return uri; } else if (resource instanceof ThemeResource) { final String uri = "theme://" @@ -57,11 +100,40 @@ public class ResourceReference extends URLReference { } - public static ResourceReference create(Resource resource) { + private static String getConnectorResourceBase(String filename, + ClientConnector connector) { + String uri = ApplicationConstants.APP_PROTOCOL_PREFIX + + ApplicationConstants.APP_REQUEST_PATH + + ConnectorResource.CONNECTOR_REQUEST_PATH + + connector.getUI().getUIId() + '/' + + connector.getConnectorId() + '/' + encodeFileName(filename); + return uri; + } + + public static String encodeFileName(String filename) { + // #7738 At least Tomcat and JBoss refuses requests containing + // encoded slashes or backslashes in URLs. Application resource URLs + // should really be passed in another way than as part of the path + // in the future. + return urlEncode(filename).replace("%2F", "/").replace("%5C", "\\"); + } + + static String urlEncode(String filename) { + try { + return URLEncoder.encode(filename, "UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException( + "UTF-8 charset not available (\"this should never happen\")", + e); + } + } + + public static ResourceReference create(Resource resource, + ClientConnector connector, String key) { if (resource == null) { return null; } else { - return new ResourceReference(resource); + return new ResourceReference(resource, connector, key); } } diff --git a/server/src/com/vaadin/server/StreamResource.java b/server/src/com/vaadin/server/StreamResource.java index 443831129f..26514c9353 100644 --- a/server/src/com/vaadin/server/StreamResource.java +++ b/server/src/com/vaadin/server/StreamResource.java @@ -19,7 +19,6 @@ package com.vaadin.server; import java.io.InputStream; import java.io.Serializable; -import com.vaadin.Application; import com.vaadin.service.FileTypeResolver; /** @@ -32,7 +31,7 @@ import com.vaadin.service.FileTypeResolver; * @since 3.0 */ @SuppressWarnings("serial") -public class StreamResource implements ApplicationResource { +public class StreamResource implements ConnectorResource { /** * Source stream the downloaded content is fetched from. @@ -50,11 +49,6 @@ public class StreamResource implements ApplicationResource { private String filename; /** - * Application. - */ - private final Application application; - - /** * Default buffer size for this stream resource. */ private int bufferSize = 0; @@ -62,7 +56,7 @@ public class StreamResource implements ApplicationResource { /** * Default cache time for this stream resource. */ - private long cacheTime = DEFAULT_CACHETIME; + private long cacheTime = DownloadStream.DEFAULT_CACHETIME; /** * Creates a new stream resource for downloading from stream. @@ -74,16 +68,9 @@ public class StreamResource implements ApplicationResource { * @param application * the Application object. */ - public StreamResource(StreamSource streamSource, String filename, - Application application) { - - this.application = application; + public StreamResource(StreamSource streamSource, String filename) { setFilename(filename); setStreamSource(streamSource); - - // Register to application - application.addResource(this); - } /** @@ -149,17 +136,6 @@ public class StreamResource implements ApplicationResource { this.filename = filename; } - /** - * @see com.vaadin.server.ApplicationResource#getApplication() - */ - @Override - public Application getApplication() { - return application; - } - - /** - * @see com.vaadin.server.ApplicationResource#getStream() - */ @Override public DownloadStream getStream() { final StreamSource ss = getStreamSource(); @@ -187,8 +163,16 @@ public class StreamResource implements ApplicationResource { public InputStream getStream(); } - /* documented in superclass */ - @Override + /** + * Gets the size of the download buffer used for this resource. + * + * <p> + * If the buffer size is 0, the buffer size is decided by the terminal + * adapter. The default value is 0. + * </p> + * + * @return the size of the buffer in bytes. + */ public int getBufferSize() { return bufferSize; } @@ -203,8 +187,14 @@ public class StreamResource implements ApplicationResource { this.bufferSize = bufferSize; } - /* documented in superclass */ - @Override + /** + * Gets the length of cache expiration time. This gives the adapter the + * possibility cache streams sent to the client. The caching may be made in + * adapter or at the client if the client supports caching. Default is + * <code>DownloadStream.DEFAULT_CACHETIME</code>. + * + * @return Cache time in milliseconds. + */ public long getCacheTime() { return cacheTime; } @@ -227,4 +217,29 @@ public class StreamResource implements ApplicationResource { this.cacheTime = cacheTime; } + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj instanceof StreamResource) { + StreamResource that = (StreamResource) obj; + return getStreamSource().equals(that.getStreamSource()) + && getMIMEType().equals(that.getMIMEType()) + && String.valueOf(getFilename()).equals( + String.valueOf(that.getFilename())) + && getBufferSize() == that.getBufferSize() + && getCacheTime() == that.getCacheTime(); + } else { + return false; + } + } + + @Override + public int hashCode() { + return (int) (getStreamSource().hashCode() + 37 + * getMIMEType().hashCode() + 37 ^ 2 + * String.valueOf(getFilename()).hashCode() + 37 ^ 3 + * getBufferSize() + 37 ^ 4 * getCacheTime()); + } + } diff --git a/server/src/com/vaadin/service/ApplicationContext.java b/server/src/com/vaadin/service/ApplicationContext.java index 08e553d0e5..591704764f 100644 --- a/server/src/com/vaadin/service/ApplicationContext.java +++ b/server/src/com/vaadin/service/ApplicationContext.java @@ -18,12 +18,9 @@ package com.vaadin.service; import java.io.File; import java.io.Serializable; -import java.net.URL; import java.util.Collection; import com.vaadin.Application; -import com.vaadin.server.AbstractCommunicationManager; -import com.vaadin.server.ApplicationResource; /** * <code>ApplicationContext</code> provides information about the running @@ -86,59 +83,6 @@ public interface ApplicationContext extends Serializable { public int getMaxInactiveInterval(); /** - * Generate a URL that can be used as the relative location of e.g. an - * {@link ApplicationResource}. - * - * This method should only be called from the processing of a UIDL request, - * not from a background thread. The return value is null if used outside a - * suitable request. - * - * @deprecated this method is intended for terminal implementation only and - * is subject to change/removal from the interface (to - * {@link AbstractCommunicationManager}) - * - * @param resource - * @param urlKey - * a key for the resource that can later be extracted from a URL - * with {@link #getURLKey(URL, String)} - */ - @Deprecated - public String generateApplicationResourceURL(ApplicationResource resource, - String urlKey); - - /** - * Tests if a URL is for an application resource (APP/...). - * - * @deprecated this method is intended for terminal implementation only and - * is subject to change/removal from the interface (to - * {@link AbstractCommunicationManager}) - * - * @param context - * @param relativeUri - * @return - */ - @Deprecated - public boolean isApplicationResourceURL(URL context, String relativeUri); - - /** - * Gets the identifier (key) from an application resource URL. This key is - * the one that was given to - * {@link #generateApplicationResourceURL(ApplicationResource, String)} when - * creating the URL. - * - * @deprecated this method is intended for terminal implementation only and - * is subject to change/removal from the interface (to - * {@link AbstractCommunicationManager}) - * - * - * @param context - * @param relativeUri - * @return - */ - @Deprecated - public String getURLKey(URL context, String relativeUri); - - /** * Interface for listening to transaction events. Implement this interface * to listen to all transactions between the client and the application. * diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index 47d893cd27..045173036e 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -37,8 +37,8 @@ import com.vaadin.server.ClientConnector; import com.vaadin.server.ComponentSizeValidator; import com.vaadin.server.ErrorMessage; import com.vaadin.server.Resource; -import com.vaadin.server.ResourceReference; import com.vaadin.server.Terminal; +import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ComponentState; import com.vaadin.tools.ReflectTools; @@ -297,7 +297,7 @@ public abstract class AbstractComponent extends AbstractClientConnector */ @Override public Resource getIcon() { - return ResourceReference.getResource(getState().getIcon()); + return getResource(ComponentConstants.ICON_RESOURCE); } /** @@ -309,7 +309,7 @@ public abstract class AbstractComponent extends AbstractClientConnector */ @Override public void setIcon(Resource icon) { - getState().setIcon(ResourceReference.create(icon)); + setResource(ComponentConstants.ICON_RESOURCE, icon); } /* diff --git a/server/src/com/vaadin/ui/AbstractEmbedded.java b/server/src/com/vaadin/ui/AbstractEmbedded.java index f3256dcde6..d94f62120f 100644 --- a/server/src/com/vaadin/ui/AbstractEmbedded.java +++ b/server/src/com/vaadin/ui/AbstractEmbedded.java @@ -5,7 +5,6 @@ package com.vaadin.ui; import com.vaadin.server.Resource; -import com.vaadin.server.ResourceReference; import com.vaadin.shared.ui.AbstractEmbeddedState; /** @@ -32,12 +31,7 @@ public abstract class AbstractEmbedded extends AbstractComponent { * the source to set. */ public void setSource(Resource source) { - if (source == null) { - getState().setSource(null); - } else { - getState().setSource(new ResourceReference(source)); - } - requestRepaint(); + setResource(AbstractEmbeddedState.SOURCE_RESOURCE, source); } /** @@ -46,12 +40,7 @@ public abstract class AbstractEmbedded extends AbstractComponent { * @return the source */ public Resource getSource() { - ResourceReference ref = ((ResourceReference) getState().getSource()); - if (ref == null) { - return null; - } else { - return ref.getResource(); - } + return getResource(AbstractEmbeddedState.SOURCE_RESOURCE); } /** diff --git a/server/src/com/vaadin/ui/AbstractMedia.java b/server/src/com/vaadin/ui/AbstractMedia.java index f9eb67f666..940d85a8b9 100644 --- a/server/src/com/vaadin/ui/AbstractMedia.java +++ b/server/src/com/vaadin/ui/AbstractMedia.java @@ -16,11 +16,18 @@ package com.vaadin.ui; +import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import com.vaadin.server.ConnectorResource; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; +import com.vaadin.server.WrappedRequest; +import com.vaadin.server.WrappedResponse; import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AbstractMediaState; import com.vaadin.shared.ui.MediaControl; @@ -64,11 +71,43 @@ public abstract class AbstractMedia extends AbstractComponent { */ public void addSource(Resource source) { if (source != null) { - getState().getSources().add(new ResourceReference(source)); + List<URLReference> sources = getState().getSources(); + sources.add(new ResourceReference(source, this, Integer + .toString(sources.size()))); getState().getSourceTypes().add(source.getMIMEType()); } } + @Override + public boolean handleConnectorRequest(WrappedRequest request, + WrappedResponse response, String path) throws IOException { + Matcher matcher = Pattern.compile("(\\d+)(/.*)?").matcher(path); + if (matcher.matches()) { + List<URLReference> sources = getState().getSources(); + + int sourceIndex = Integer.parseInt(matcher.group(1)); + + if (sourceIndex < 0 || sourceIndex >= sources.size()) { + getLogger().warning( + "Requested source index " + sourceIndex + + " is out of bounds"); + return false; + } + + URLReference reference = sources.get(sourceIndex); + ConnectorResource resource = (ConnectorResource) ResourceReference + .getResource(reference); + resource.getStream().writeTo(response); + return true; + } else { + return super.handleConnectorRequest(request, response, path); + } + } + + private Logger getLogger() { + return Logger.getLogger(AbstractMedia.class.getName()); + } + /** * Set multiple sources at once. Which of the sources is used is selected by * the browser depending on which file formats it supports. See <a diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index 265099f372..3140c26525 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -28,6 +28,7 @@ import java.util.logging.Logger; import com.vaadin.server.AbstractClientConnector; import com.vaadin.server.AbstractCommunicationManager; import com.vaadin.server.ClientConnector; +import com.vaadin.server.GlobalResourceHandler; /** * A class which takes care of book keeping of {@link ClientConnector}s for a @@ -141,11 +142,22 @@ public class ConnectorTracker implements Serializable { getLogger().fine( "Unregistered " + connector.getClass().getSimpleName() + " (" + connectorId + ")"); + + removeFromGlobalResourceHandler(connector); connectorIdToConnector.remove(connectorId); uninitializedConnectors.remove(connector); diffStates.remove(connector); } + private void removeFromGlobalResourceHandler(ClientConnector connector) { + GlobalResourceHandler globalResourceHandler = uI.getApplication() + .getGlobalResourceHandler(false); + // Nothing to do if there is no handler + if (globalResourceHandler != null) { + globalResourceHandler.unregisterConnector(connector); + } + } + /** * Checks whether the given connector has already been initialized in the * browser. The given connector should be registered with this connector @@ -224,6 +236,8 @@ public class ConnectorTracker implements Serializable { "cleanConnectorMap unregistered connector " + getConnectorAndParentInfo(connector) + "). This should have been done when the connector was detached."); + + removeFromGlobalResourceHandler(connector); uninitializedConnectors.remove(connector); diffStates.remove(connector); iterator.remove(); diff --git a/server/src/com/vaadin/ui/Link.java b/server/src/com/vaadin/ui/Link.java index e98b558dbe..a2737e4483 100644 --- a/server/src/com/vaadin/ui/Link.java +++ b/server/src/com/vaadin/ui/Link.java @@ -18,11 +18,12 @@ package com.vaadin.ui; import java.util.Map; +import com.vaadin.server.LegacyComponent; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.server.Resource; -import com.vaadin.server.LegacyComponent; import com.vaadin.shared.ui.BorderStyle; +import com.vaadin.shared.ui.link.LinkConstants; /** * Link is used to create external or internal URL links. @@ -45,8 +46,6 @@ public class Link extends AbstractComponent implements LegacyComponent { @Deprecated public static final BorderStyle TARGET_BORDER_DEFAULT = BorderStyle.DEFAULT; - private Resource resource = null; - private String targetName; private BorderStyle targetBorder = BorderStyle.DEFAULT; @@ -70,7 +69,7 @@ public class Link extends AbstractComponent implements LegacyComponent { */ public Link(String caption, Resource resource) { setCaption(caption); - this.resource = resource; + setResource(resource); } /** @@ -94,7 +93,7 @@ public class Link extends AbstractComponent implements LegacyComponent { public Link(String caption, Resource resource, String targetName, int width, int height, BorderStyle border) { setCaption(caption); - this.resource = resource; + setResource(resource); setTargetName(targetName); setTargetWidth(width); setTargetHeight(height); @@ -111,10 +110,7 @@ public class Link extends AbstractComponent implements LegacyComponent { */ @Override public void paintContent(PaintTarget target) throws PaintException { - - if (resource != null) { - target.addAttribute("src", resource); - } else { + if (getResource() == null) { return; } @@ -230,7 +226,7 @@ public class Link extends AbstractComponent implements LegacyComponent { * @return the Resource. */ public Resource getResource() { - return resource; + return getResource(LinkConstants.HREF_RESOURCE); } /** @@ -240,8 +236,7 @@ public class Link extends AbstractComponent implements LegacyComponent { * the resource to set. */ public void setResource(Resource resource) { - this.resource = resource; - markAsDirty(); + setResource(LinkConstants.HREF_RESOURCE, resource); } @Override diff --git a/server/src/com/vaadin/ui/LoginForm.java b/server/src/com/vaadin/ui/LoginForm.java index 7715f08a6e..0d89fd19a3 100644 --- a/server/src/com/vaadin/ui/LoginForm.java +++ b/server/src/com/vaadin/ui/LoginForm.java @@ -25,7 +25,7 @@ import java.util.Iterator; import java.util.Map; import com.vaadin.Application; -import com.vaadin.server.ApplicationResource; +import com.vaadin.server.ConnectorResource; import com.vaadin.server.DownloadStream; import com.vaadin.server.RequestHandler; import com.vaadin.server.WrappedRequest; @@ -58,23 +58,7 @@ public class LoginForm extends CustomComponent { private Embedded iframe = new Embedded(); - private ApplicationResource loginPage = new ApplicationResource() { - - @Override - public Application getApplication() { - return LoginForm.this.getApplication(); - } - - @Override - public int getBufferSize() { - return getLoginHTML().length; - } - - @Override - public long getCacheTime() { - return -1; - } - + private ConnectorResource loginPage = new ConnectorResource() { @Override public String getFilename() { return "login"; @@ -82,8 +66,13 @@ public class LoginForm extends CustomComponent { @Override public DownloadStream getStream() { - return new DownloadStream(new ByteArrayInputStream(getLoginHTML()), - getMIMEType(), getFilename()); + byte[] loginHTML = getLoginHTML(); + DownloadStream downloadStream = new DownloadStream( + new ByteArrayInputStream(loginHTML), getMIMEType(), + getFilename()); + downloadStream.setBufferSize(loginHTML.length); + downloadStream.setCacheTime(-1); + return downloadStream; } @Override @@ -197,14 +186,12 @@ public class LoginForm extends CustomComponent { @Override public void attach() { super.attach(); - getApplication().addResource(loginPage); getApplication().addRequestHandler(requestHandler); iframe.setSource(loginPage); } @Override public void detach() { - getApplication().removeResource(loginPage); getApplication().removeRequestHandler(requestHandler); super.detach(); diff --git a/server/src/com/vaadin/ui/Video.java b/server/src/com/vaadin/ui/Video.java index d14ed71270..2d83538d57 100644 --- a/server/src/com/vaadin/ui/Video.java +++ b/server/src/com/vaadin/ui/Video.java @@ -17,7 +17,7 @@ package com.vaadin.ui; import com.vaadin.server.Resource; -import com.vaadin.server.ResourceReference; +import com.vaadin.shared.ui.video.VideoConstants; import com.vaadin.shared.ui.video.VideoState; /** @@ -79,14 +79,14 @@ public class Video extends AbstractMedia { * @param poster */ public void setPoster(Resource poster) { - getState().setPoster(ResourceReference.create(poster)); + setResource(VideoConstants.POSTER_RESOURCE, poster); } /** * @return The poster image. */ public Resource getPoster() { - return ResourceReference.getResource(getState().getPoster()); + return getResource(VideoConstants.POSTER_RESOURCE); } } diff --git a/shared/src/com/vaadin/shared/ComponentConstants.java b/shared/src/com/vaadin/shared/ComponentConstants.java new file mode 100644 index 0000000000..6fea3f2dfd --- /dev/null +++ b/shared/src/com/vaadin/shared/ComponentConstants.java @@ -0,0 +1,21 @@ +/* + * 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.shared; + +public class ComponentConstants { + public static final String ICON_RESOURCE = "icon"; +} diff --git a/shared/src/com/vaadin/shared/ComponentState.java b/shared/src/com/vaadin/shared/ComponentState.java index 1b08f9b2d2..898114e1db 100644 --- a/shared/src/com/vaadin/shared/ComponentState.java +++ b/shared/src/com/vaadin/shared/ComponentState.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Set; import com.vaadin.shared.communication.SharedState; -import com.vaadin.shared.communication.URLReference; /** * Default shared state implementation for UI components. @@ -40,7 +39,6 @@ public class ComponentState extends SharedState { // string! private String caption = null; private boolean visible = true; - private URLReference icon = null; private List<String> styles = null; private String id = null; /** @@ -265,14 +263,6 @@ public class ComponentState extends SharedState { this.visible = visible; } - public URLReference getIcon() { - return icon; - } - - public void setIcon(URLReference icon) { - this.icon = icon; - } - /** * Gets the style names for the component. * diff --git a/shared/src/com/vaadin/shared/communication/SharedState.java b/shared/src/com/vaadin/shared/communication/SharedState.java index f9c9f642d0..f35d4675f3 100644 --- a/shared/src/com/vaadin/shared/communication/SharedState.java +++ b/shared/src/com/vaadin/shared/communication/SharedState.java @@ -17,6 +17,8 @@ package com.vaadin.shared.communication; import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; import com.vaadin.shared.Connector; @@ -48,6 +50,15 @@ import com.vaadin.shared.Connector; */ public class SharedState implements Serializable { + /** + * The automatically managed resources used by the connector. + * + * @see com.vaadin.terminal.AbstractClientConnector#setResource(String, + * com.vaadin.terminal.Resource) + * @see com.vaadin.terminal.gwt.client.ui.AbstractConnector#getResourceUrl(String) + */ + public Map<String, URLReference> resources = new HashMap<String, URLReference>(); + private boolean enabled = true; /** diff --git a/shared/src/com/vaadin/shared/ui/AbstractEmbeddedState.java b/shared/src/com/vaadin/shared/ui/AbstractEmbeddedState.java index 96b785edd0..6422b643ad 100644 --- a/shared/src/com/vaadin/shared/ui/AbstractEmbeddedState.java +++ b/shared/src/com/vaadin/shared/ui/AbstractEmbeddedState.java @@ -1,20 +1,12 @@ package com.vaadin.shared.ui; import com.vaadin.shared.ComponentState; -import com.vaadin.shared.communication.URLReference; public class AbstractEmbeddedState extends ComponentState { - protected URLReference source; - protected String alternateText; + public static final String SOURCE_RESOURCE = "source"; - public URLReference getSource() { - return source; - } - - public void setSource(URLReference source) { - this.source = source; - } + private String alternateText; public String getAlternateText() { return alternateText; diff --git a/shared/src/com/vaadin/shared/ui/link/LinkConstants.java b/shared/src/com/vaadin/shared/ui/link/LinkConstants.java new file mode 100644 index 0000000000..61fc73577f --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/link/LinkConstants.java @@ -0,0 +1,23 @@ +/* + * 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.shared.ui.link; + +import com.vaadin.shared.ComponentState; + +public class LinkConstants { + public static String HREF_RESOURCE = "href"; +} diff --git a/shared/src/com/vaadin/shared/ui/video/VideoConstants.java b/shared/src/com/vaadin/shared/ui/video/VideoConstants.java new file mode 100644 index 0000000000..951495b6af --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/video/VideoConstants.java @@ -0,0 +1,21 @@ +/* + * 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.shared.ui.video; + +public class VideoConstants { + public static final String POSTER_RESOURCE = "poster"; +} diff --git a/shared/src/com/vaadin/shared/ui/video/VideoState.java b/shared/src/com/vaadin/shared/ui/video/VideoState.java index 695d495e16..2e6e24a743 100644 --- a/shared/src/com/vaadin/shared/ui/video/VideoState.java +++ b/shared/src/com/vaadin/shared/ui/video/VideoState.java @@ -15,18 +15,8 @@ */ package com.vaadin.shared.ui.video; -import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.AbstractMediaState; public class VideoState extends AbstractMediaState { - private URLReference poster; - - public URLReference getPoster() { - return poster; - } - - public void setPoster(URLReference poster) { - this.poster = poster; - } } diff --git a/tests/server-side/com/vaadin/tests/server/TestMimeTypes.java b/tests/server-side/com/vaadin/tests/server/TestMimeTypes.java index 7bb1fd0050..dc730f6cc4 100644 --- a/tests/server-side/com/vaadin/tests/server/TestMimeTypes.java +++ b/tests/server-side/com/vaadin/tests/server/TestMimeTypes.java @@ -17,10 +17,10 @@ public class TestMimeTypes extends TestCase { } }; - Embedded e = new Embedded("A pdf", new ClassResource("file.pddf", app)); + Embedded e = new Embedded("A pdf", new ClassResource("file.pddf")); assertEquals("Invalid mimetype", "application/octet-stream", e.getMimeType()); - e = new Embedded("A pdf", new ClassResource("file.pdf", app)); + e = new Embedded("A pdf", new ClassResource("file.pdf")); assertEquals("Invalid mimetype", "application/pdf", e.getMimeType()); } } diff --git a/tests/testbench/com/vaadin/tests/LayoutDemo.java b/tests/testbench/com/vaadin/tests/LayoutDemo.java index 3348eda7a4..a5331b94af 100644 --- a/tests/testbench/com/vaadin/tests/LayoutDemo.java +++ b/tests/testbench/com/vaadin/tests/LayoutDemo.java @@ -25,8 +25,8 @@ import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Layout; import com.vaadin.ui.Panel; -import com.vaadin.ui.UI.LegacyWindow; import com.vaadin.ui.TabSheet; +import com.vaadin.ui.UI.LegacyWindow; import com.vaadin.ui.VerticalLayout; /** @@ -121,7 +121,7 @@ public class LayoutDemo extends com.vaadin.Application.LegacyApplication { private Component getExamplePicture(String caption) { // loads image from package com.vaadin.demo - final ClassResource cr = new ClassResource("m-bullet-blue.gif", this); + final ClassResource cr = new ClassResource("m-bullet-blue.gif"); final Embedded em = new Embedded("Embedded " + caption, cr); em.setWidth("170px"); return em; diff --git a/tests/testbench/com/vaadin/tests/TestCaptionWrapper.java b/tests/testbench/com/vaadin/tests/TestCaptionWrapper.java index 57a98f787f..b2e09a2a6f 100644 --- a/tests/testbench/com/vaadin/tests/TestCaptionWrapper.java +++ b/tests/testbench/com/vaadin/tests/TestCaptionWrapper.java @@ -92,10 +92,8 @@ public class TestCaptionWrapper extends CustomComponent implements Listener { final VerticalLayout tab2 = new VerticalLayout(); test(tab2); populateLayout(tab2); - tabsheet.addTab(tab1, "TabSheet tab1", new ClassResource("m.gif", - getApplication())); - tabsheet.addTab(tab2, "TabSheet tab2", new ClassResource("m.gif", - getApplication())); + tabsheet.addTab(tab1, "TabSheet tab1", new ClassResource("m.gif")); + tabsheet.addTab(tab2, "TabSheet tab2", new ClassResource("m.gif")); final VerticalLayout expandLayout = new VerticalLayout(); test(expandLayout); @@ -186,7 +184,7 @@ public class TestCaptionWrapper extends CustomComponent implements Listener { * @param c */ void test(AbstractComponent c) { - final ClassResource res = new ClassResource("m.gif", getApplication()); + final ClassResource res = new ClassResource("m.gif"); final ErrorMessage errorMsg = new UserError("User error " + c); if ((c.getCaption() == null) || (c.getCaption().length() <= 0)) { diff --git a/tests/testbench/com/vaadin/tests/TestDateField.java b/tests/testbench/com/vaadin/tests/TestDateField.java index 67b2f2cc69..5854b52b73 100644 --- a/tests/testbench/com/vaadin/tests/TestDateField.java +++ b/tests/testbench/com/vaadin/tests/TestDateField.java @@ -77,8 +77,7 @@ public class TestDateField extends CustomComponent { @Override public void attach() { - final ClassResource res = new ClassResource("m.gif", - super.getApplication()); + final ClassResource res = new ClassResource("m.gif"); df.setIcon(res); super.attach(); } diff --git a/tests/testbench/com/vaadin/tests/TestForStyledUpload.java b/tests/testbench/com/vaadin/tests/TestForStyledUpload.java index a6fdaf5643..14fe7902aa 100644 --- a/tests/testbench/com/vaadin/tests/TestForStyledUpload.java +++ b/tests/testbench/com/vaadin/tests/TestForStyledUpload.java @@ -189,7 +189,7 @@ public class TestForStyledUpload extends Application.LegacyApplication + " bytes.", ContentMode.XHTML)); status.addComponent(new Link("Download " + buffer.getFileName(), - new StreamResource(buffer, buffer.getFileName(), this))); + new StreamResource(buffer, buffer.getFileName()))); status.setVisible(true); } diff --git a/tests/testbench/com/vaadin/tests/TestForUpload.java b/tests/testbench/com/vaadin/tests/TestForUpload.java index eb59ed9f1f..b0697d337d 100644 --- a/tests/testbench/com/vaadin/tests/TestForUpload.java +++ b/tests/testbench/com/vaadin/tests/TestForUpload.java @@ -160,7 +160,7 @@ public class TestForUpload extends CustomComponent implements status.addComponent(new Link("Download " + buffer.getFileName(), new StreamResource(buffer, - buffer.getFileName(), getApplication()))); + buffer.getFileName()))); status.setVisible(true); } diff --git a/tests/testbench/com/vaadin/tests/appengine/GAESyncTest.java b/tests/testbench/com/vaadin/tests/appengine/GAESyncTest.java index 26bf90948b..a7d2b03415 100644 --- a/tests/testbench/com/vaadin/tests/appengine/GAESyncTest.java +++ b/tests/testbench/com/vaadin/tests/appengine/GAESyncTest.java @@ -12,8 +12,8 @@ import com.vaadin.ui.Embedded; import com.vaadin.ui.GridLayout; import com.vaadin.ui.Label; import com.vaadin.ui.Notification; -import com.vaadin.ui.UI.LegacyWindow; import com.vaadin.ui.TextField; +import com.vaadin.ui.UI.LegacyWindow; public class GAESyncTest extends Application.LegacyApplication { @@ -116,7 +116,7 @@ public class GAESyncTest extends Application.LegacyApplication { } private void addImage() { - ClassResource res = new ClassResource("img1.png", app) { + ClassResource res = new ClassResource("img1.png") { private static final long serialVersionUID = 1L; diff --git a/tests/testbench/com/vaadin/tests/application/ThreadLocalInstances.java b/tests/testbench/com/vaadin/tests/application/ThreadLocalInstances.java index f31ea76728..0e7dd1b242 100644 --- a/tests/testbench/com/vaadin/tests/application/ThreadLocalInstances.java +++ b/tests/testbench/com/vaadin/tests/application/ThreadLocalInstances.java @@ -2,7 +2,6 @@ package com.vaadin.tests.application; import com.vaadin.Application; import com.vaadin.UIRequiresMoreInformationException; -import com.vaadin.server.ApplicationResource; import com.vaadin.server.DownloadStream; import com.vaadin.server.PaintException; import com.vaadin.server.WrappedRequest; @@ -47,7 +46,7 @@ public class ThreadLocalInstances extends AbstractTestApplication { } }; - private final ApplicationResource resource = new FlagSeResource(this) { + private final FlagSeResource resource = new FlagSeResource() { @Override public DownloadStream getStream() { reportCurrentStatus("resource handler"); diff --git a/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedImageRefresh.java b/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedImageRefresh.java index 254d27c1dd..52454e115e 100644 --- a/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedImageRefresh.java +++ b/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedImageRefresh.java @@ -38,7 +38,7 @@ public class EmbeddedImageRefresh extends TestBase { // Attach it to a resource. final MyImageSource imageSource = new MyImageSource(); final StreamResource imageResource = new StreamResource(imageSource, - "testimage.png", this); + "testimage.png"); imageResource.setCacheTime(0); embedded.setSource(imageResource); diff --git a/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedPdf.java b/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedPdf.java index ae98a55348..025245ab64 100644 --- a/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedPdf.java +++ b/tests/testbench/com/vaadin/tests/components/embedded/EmbeddedPdf.java @@ -25,7 +25,7 @@ public class EmbeddedPdf extends TestBase { player.setType(Embedded.TYPE_BROWSER); player.setWidth("400px"); player.setHeight("300px"); - player.setSource(new ClassResource(getClass(), "test.pdf", this)); + player.setSource(new ClassResource(getClass(), "test.pdf")); addComponent(player); addComponent(new Button("Remove pdf", new Button.ClickListener() { diff --git a/tests/testbench/com/vaadin/tests/components/embeddedbrowser/EmbeddedBrowserIsVisible.java b/tests/testbench/com/vaadin/tests/components/embeddedbrowser/EmbeddedBrowserIsVisible.java index b15f33f633..2eaf712676 100644 --- a/tests/testbench/com/vaadin/tests/components/embeddedbrowser/EmbeddedBrowserIsVisible.java +++ b/tests/testbench/com/vaadin/tests/components/embeddedbrowser/EmbeddedBrowserIsVisible.java @@ -35,17 +35,18 @@ public class EmbeddedBrowserIsVisible extends TestBase { browser.setAlternateText("Browser alternative text"); final TextSource textSource = new TextSource("initial"); final StreamResource textResource = new StreamResource(textSource, - "initial.txt", this); + "initial.txt"); textResource.setMIMEType("text/plain"); browser.setSource(textResource); addComponent(browser); page1.addListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { TextSource helloSource = new TextSource("Hello World"); StreamResource helloResource = new StreamResource(helloSource, - "helloworld.txt", EmbeddedBrowserIsVisible.this); + "helloworld.txt"); helloResource.setMIMEType("text/plain"); browser.setSource(helloResource); } @@ -53,10 +54,11 @@ public class EmbeddedBrowserIsVisible extends TestBase { page2.addListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { TextSource helloSource = new TextSource("Lorem Ipsum"); StreamResource helloResource = new StreamResource(helloSource, - "loremipsum.txt", EmbeddedBrowserIsVisible.this); + "loremipsum.txt"); helloResource.setMIMEType("text/plain"); browser.setSource(helloResource); } @@ -64,6 +66,7 @@ public class EmbeddedBrowserIsVisible extends TestBase { page3.addListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { browser.setSource(null); } @@ -87,6 +90,7 @@ public class EmbeddedBrowserIsVisible extends TestBase { this.text = text; } + @Override public InputStream getStream() { StringBuilder sb = new StringBuilder(); diff --git a/tests/testbench/com/vaadin/tests/components/image/ImageClicks.java b/tests/testbench/com/vaadin/tests/components/image/ImageClicks.java index f1a3a09952..e2d983b7ce 100644 --- a/tests/testbench/com/vaadin/tests/components/image/ImageClicks.java +++ b/tests/testbench/com/vaadin/tests/components/image/ImageClicks.java @@ -30,10 +30,11 @@ public class ImageClicks extends TestBase { Image image = new Image(); final MyImageSource imageSource = new MyImageSource(); final StreamResource imageResource = new StreamResource(imageSource, - "testimage.png", this); + "testimage.png"); image.setSource(imageResource); image.addListener(new ClickListener() { + @Override public void click(ClickEvent event) { ++clickCounter; label.setValue(labelText()); @@ -70,6 +71,7 @@ public class ImageClicks extends TestBase { return (int) Math.round(pos * resolution / (cells * 1.0)); } + @Override public InputStream getStream() { // Create an image and draw some background on it. BufferedImage image = new BufferedImage(300, 300, diff --git a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.html b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.html index 16e74aa485..8d0bb44cb5 100644 --- a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.html +++ b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.html @@ -39,7 +39,7 @@ <tr> <td>assertText</td> <td>vaadin=runcomvaadintestscomponentsjavascriptcomponentBasicJavaScriptComponent::/VVerticalLayout[0]/VVerticalLayout[0]/JavaScriptWidget[0]/domChild[0]</td> - <td>4. Url: /run/com.vaadin.tests.components.javascriptcomponent.BasicJavaScriptComponent/APP/1/test</td> + <td>4. Url: /run/com.vaadin.tests.components.javascriptcomponent.BasicJavaScriptComponent/APP/connector/1/12/test</td> </tr> <tr> <td>assertText</td> diff --git a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java index a0189354f9..38beca3bf1 100644 --- a/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java +++ b/tests/testbench/com/vaadin/tests/components/javascriptcomponent/BasicJavaScriptComponent.java @@ -22,7 +22,8 @@ import java.util.List; import com.vaadin.annotations.JavaScript; import com.vaadin.external.json.JSONArray; import com.vaadin.external.json.JSONException; -import com.vaadin.server.ClassResource; +import com.vaadin.server.DynamicConnectorResource; +import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.WrappedRequest; import com.vaadin.shared.communication.ClientRpc; @@ -107,9 +108,8 @@ public class BasicJavaScriptComponent extends AbstractTestUI { .setMessages( Arrays.asList("First state message", "Second state message")); - ClassResource resource = new ClassResource("test", - BasicJavaScriptComponent.this.getApplication()); - getState().setUrl(new ResourceReference(resource)); + Resource resource = new DynamicConnectorResource(this, "test"); + getState().setUrl(new ResourceReference(resource, null, null)); } @Override diff --git a/tests/testbench/com/vaadin/tests/components/link/LinkToPercentage.java b/tests/testbench/com/vaadin/tests/components/link/LinkToPercentage.java index 70ddc74616..770ff9895a 100644 --- a/tests/testbench/com/vaadin/tests/components/link/LinkToPercentage.java +++ b/tests/testbench/com/vaadin/tests/components/link/LinkToPercentage.java @@ -3,7 +3,7 @@ package com.vaadin.tests.components.link; import java.io.IOException; import java.io.InputStream; -import com.vaadin.server.ApplicationResource; +import com.vaadin.server.Resource; import com.vaadin.server.StreamResource; import com.vaadin.server.StreamResource.StreamSource; import com.vaadin.tests.components.TestBase; @@ -14,7 +14,7 @@ public class LinkToPercentage extends TestBase { @Override protected void setup() { String filename = "110% Vaadin"; - ApplicationResource resource = new StreamResource(new StreamSource() { + Resource resource = new StreamResource(new StreamSource() { @Override public InputStream getStream() { return new InputStream() { @@ -31,8 +31,7 @@ public class LinkToPercentage extends TestBase { } }; } - }, filename, this); - addResource(resource); + }, filename); Link link = new Link("The link", resource); diff --git a/tests/testbench/com/vaadin/tests/components/table/TableItemIcon.java b/tests/testbench/com/vaadin/tests/components/table/TableItemIcon.java index 8b502268b1..1faa9667c2 100644 --- a/tests/testbench/com/vaadin/tests/components/table/TableItemIcon.java +++ b/tests/testbench/com/vaadin/tests/components/table/TableItemIcon.java @@ -27,11 +27,9 @@ public class TableItemIcon extends TestBase { getLayout().addComponent(table); Item item = table.addItem("FI"); - item.getItemProperty("icon").setValue( - new ClassResource("fi.gif", TableItemIcon.this)); + item.getItemProperty("icon").setValue(new ClassResource("fi.gif")); item = table.addItem("SE"); - item.getItemProperty("icon").setValue( - new ClassResource("se.gif", TableItemIcon.this)); + item.getItemProperty("icon").setValue(new ClassResource("se.gif")); } diff --git a/tests/testbench/com/vaadin/tests/dd/DDTest6.java b/tests/testbench/com/vaadin/tests/dd/DDTest6.java index 05be14eb76..a4c566d75a 100644 --- a/tests/testbench/com/vaadin/tests/dd/DDTest6.java +++ b/tests/testbench/com/vaadin/tests/dd/DDTest6.java @@ -229,7 +229,7 @@ public class DDTest6 extends TestBase { return null; } }; - return new StreamResource(streamSource, getName(), DDTest6.get()); + return new StreamResource(streamSource, getName()); } } diff --git a/tests/testbench/com/vaadin/tests/dd/StartHtml5Drag.java b/tests/testbench/com/vaadin/tests/dd/StartHtml5Drag.java index 592ea92f80..609b2fe4fc 100644 --- a/tests/testbench/com/vaadin/tests/dd/StartHtml5Drag.java +++ b/tests/testbench/com/vaadin/tests/dd/StartHtml5Drag.java @@ -44,8 +44,7 @@ public class StartHtml5Drag extends TestBase { }); addComponent(dropTarget); - Embedded iframe = new Embedded("", new ClassResource("html5drop.htm", - this)); + Embedded iframe = new Embedded("", new ClassResource("html5drop.htm")); iframe.setType(Embedded.TYPE_BROWSER); iframe.setWidth(400, Sizeable.UNITS_PIXELS); iframe.setHeight(400, Sizeable.UNITS_PIXELS); diff --git a/tests/testbench/com/vaadin/tests/integration/FlagSeResource.java b/tests/testbench/com/vaadin/tests/integration/FlagSeResource.java index 76adecb7d8..6c2fcdc2d4 100644 --- a/tests/testbench/com/vaadin/tests/integration/FlagSeResource.java +++ b/tests/testbench/com/vaadin/tests/integration/FlagSeResource.java @@ -1,17 +1,16 @@ package com.vaadin.tests.integration; -import com.vaadin.Application; import com.vaadin.server.ClassResource; public class FlagSeResource extends ClassResource { - public FlagSeResource(Application application) { + public FlagSeResource() { super("/" + FlagSeResource.class .getName() .replace('.', '/') .replaceAll(FlagSeResource.class.getSimpleName() + "$", - "") + "se.gif", application); + "") + "se.gif"); } } diff --git a/tests/testbench/com/vaadin/tests/integration/IntegrationTestApplication.java b/tests/testbench/com/vaadin/tests/integration/IntegrationTestApplication.java index b089515b0e..9c7225b510 100644 --- a/tests/testbench/com/vaadin/tests/integration/IntegrationTestApplication.java +++ b/tests/testbench/com/vaadin/tests/integration/IntegrationTestApplication.java @@ -7,8 +7,8 @@ import com.vaadin.data.Property.ValueChangeListener; import com.vaadin.server.ClassResource; import com.vaadin.server.Resource; import com.vaadin.ui.Label; -import com.vaadin.ui.UI.LegacyWindow; import com.vaadin.ui.Table; +import com.vaadin.ui.UI.LegacyWindow; public class IntegrationTestApplication extends Application.LegacyApplication { @@ -28,11 +28,10 @@ public class IntegrationTestApplication extends Application.LegacyApplication { window.addComponent(table); Item item = table.addItem("FI"); - item.getItemProperty("icon") - .setValue(new ClassResource("fi.gif", this)); + item.getItemProperty("icon").setValue(new ClassResource("fi.gif")); item.getItemProperty("country").setValue("Finland"); item = table.addItem("SE"); - item.getItemProperty("icon").setValue(new FlagSeResource(this)); + item.getItemProperty("icon").setValue(new FlagSeResource()); item.getItemProperty("country").setValue("Sweden"); final Label selectedLabel = new Label(); diff --git a/tests/testbench/com/vaadin/tests/integration/JSR286PortletApplication.java b/tests/testbench/com/vaadin/tests/integration/JSR286PortletApplication.java index d2c15053ef..2243adf336 100644 --- a/tests/testbench/com/vaadin/tests/integration/JSR286PortletApplication.java +++ b/tests/testbench/com/vaadin/tests/integration/JSR286PortletApplication.java @@ -28,9 +28,9 @@ import com.vaadin.ui.Embedded; import com.vaadin.ui.Label; import com.vaadin.ui.Link; import com.vaadin.ui.Notification; +import com.vaadin.ui.TextField; import com.vaadin.ui.UI; import com.vaadin.ui.UI.LegacyWindow; -import com.vaadin.ui.TextField; import com.vaadin.ui.Upload; import com.vaadin.ui.Upload.Receiver; @@ -57,11 +57,11 @@ public class JSR286PortletApplication extends Application.LegacyApplication { Embedded appResourceTest = new Embedded( "Test of ApplicationResources with full path", - new FlagSeResource(this)); + new FlagSeResource()); main.addComponent(appResourceTest); Embedded specialNameResourceTest = new Embedded( "Test ApplicationResources with special names", - new SpecialNameResource(this)); + new SpecialNameResource()); specialNameResourceTest.addStyleName("hugeBorder"); main.addComponent(specialNameResourceTest); diff --git a/tests/testbench/com/vaadin/tests/integration/SpecialNameResource.java b/tests/testbench/com/vaadin/tests/integration/SpecialNameResource.java index 3a0bf7cdd5..3806e61c36 100644 --- a/tests/testbench/com/vaadin/tests/integration/SpecialNameResource.java +++ b/tests/testbench/com/vaadin/tests/integration/SpecialNameResource.java @@ -1,10 +1,9 @@ package com.vaadin.tests.integration; -import com.vaadin.Application; import com.vaadin.server.ClassResource; public class SpecialNameResource extends ClassResource { - public SpecialNameResource(Application application) { - super("spe=cial%res&ource.gif", application); + public SpecialNameResource() { + super("spe=cial%res&ource.gif"); } } diff --git a/tests/testbench/com/vaadin/tests/minitutorials/v7a2/ResourceInStateComponent.java b/tests/testbench/com/vaadin/tests/minitutorials/v7a2/ResourceInStateComponent.java index 3ec8dd3664..cf0c5a3e2b 100644 --- a/tests/testbench/com/vaadin/tests/minitutorials/v7a2/ResourceInStateComponent.java +++ b/tests/testbench/com/vaadin/tests/minitutorials/v7a2/ResourceInStateComponent.java @@ -17,7 +17,6 @@ package com.vaadin.tests.minitutorials.v7a2; import com.vaadin.server.Resource; -import com.vaadin.server.ResourceReference; import com.vaadin.tests.widgetset.client.minitutorials.v7a2.ResourceInStateState; import com.vaadin.ui.AbstractComponent; @@ -28,10 +27,10 @@ public class ResourceInStateComponent extends AbstractComponent { } public void setMyIcon(Resource icon) { - getState().setMyIcon(new ResourceReference(icon)); + setResource(ResourceInStateState.MY_ICON_RESOURCE, icon); } public Resource getMyIcon() { - return ResourceReference.getResource(getState().getMyIcon()); + return getResource(ResourceInStateState.MY_ICON_RESOURCE); } } diff --git a/tests/testbench/com/vaadin/tests/requesthandlers/AppResource404.java b/tests/testbench/com/vaadin/tests/requesthandlers/AppResource404.java index 6c3dcafc74..ebccba74fb 100644 --- a/tests/testbench/com/vaadin/tests/requesthandlers/AppResource404.java +++ b/tests/testbench/com/vaadin/tests/requesthandlers/AppResource404.java @@ -3,8 +3,6 @@ package com.vaadin.tests.requesthandlers; import com.vaadin.server.ExternalResource; import com.vaadin.tests.components.TestBase; import com.vaadin.tests.integration.FlagSeResource; -import com.vaadin.ui.Button; -import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Link; public class AppResource404 extends TestBase { @@ -12,21 +10,12 @@ public class AppResource404 extends TestBase { @Override protected void setup() { // Add one existing resource - final FlagSeResource resource = new FlagSeResource(this); + final FlagSeResource resource = new FlagSeResource(); resource.setCacheTime(0); - addResource(resource); addComponent(new Link("Existing resource", resource)); addComponent(new Link("Non-existing resource", new ExternalResource( getURL().toString() + "APP/12341234/"))); - - addComponent(new Button("Remove existing resrouce", - new Button.ClickListener() { - @Override - public void buttonClick(ClickEvent event) { - removeResource(resource); - } - })); } @Override diff --git a/tests/testbench/com/vaadin/tests/resources/ConnectorResourceTest.html b/tests/testbench/com/vaadin/tests/resources/ConnectorResourceTest.html new file mode 100644 index 0000000000..f400bacffa --- /dev/null +++ b/tests/testbench/com/vaadin/tests/resources/ConnectorResourceTest.html @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.resources.ConnectorResourceTest?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td></td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/tests/testbench/com/vaadin/tests/resources/ConnectorResourceTest.java b/tests/testbench/com/vaadin/tests/resources/ConnectorResourceTest.java new file mode 100644 index 0000000000..c8d711f0b2 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/resources/ConnectorResourceTest.java @@ -0,0 +1,71 @@ +package com.vaadin.tests.resources; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.HashMap; + +import javax.imageio.ImageIO; + +import com.vaadin.server.DynamicConnectorResource; +import com.vaadin.server.WrappedRequest; +import com.vaadin.server.WrappedResponse; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Embedded; + +public class ConnectorResourceTest extends AbstractTestUI { + + private static final String DYNAMIC_IMAGE_NAME = "requestImage.png"; + + @Override + protected void setup(WrappedRequest request) { + addComponent(new Embedded(DYNAMIC_IMAGE_NAME, + new DynamicConnectorResource(this, DYNAMIC_IMAGE_NAME))); + addComponent(new Embedded("Dynamic text", new DynamicConnectorResource( + this, DYNAMIC_IMAGE_NAME, new HashMap<String, String>() { + { + put("text", "Dynamic%20text"); + } + }))); + } + + @Override + protected String getTestDescription() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected Integer getTicketNumber() { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean handleConnectorRequest(WrappedRequest request, + WrappedResponse response, String path) throws IOException { + if (DYNAMIC_IMAGE_NAME.equals(path)) { + // Create an image, draw the "text" parameter to it and output it to + // the browser. + String text = request.getParameter("text"); + if (text == null) { + text = DYNAMIC_IMAGE_NAME; + } + BufferedImage bi = getImage(text); + response.setContentType("image/png"); + ImageIO.write(bi, "png", response.getOutputStream()); + + return true; + } else { + return super.handleConnectorRequest(request, response, path); + } + } + + private BufferedImage getImage(String text) { + BufferedImage bi = new BufferedImage(150, 30, + BufferedImage.TYPE_3BYTE_BGR); + bi.getGraphics() + .drawChars(text.toCharArray(), 0, text.length(), 10, 20); + return bi; + } + +} diff --git a/tests/testbench/com/vaadin/tests/resources/DownloadLargeFileResource.java b/tests/testbench/com/vaadin/tests/resources/DownloadLargeFileResource.java index 00f1e4d2d6..d799e1a8d4 100644 --- a/tests/testbench/com/vaadin/tests/resources/DownloadLargeFileResource.java +++ b/tests/testbench/com/vaadin/tests/resources/DownloadLargeFileResource.java @@ -51,7 +51,7 @@ public class DownloadLargeFileResource extends TestBase { os.write(b); } os.close(); - hugeFileResource = new FileResource(hugeFile, this); + hugeFileResource = new FileResource(hugeFile); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); diff --git a/tests/testbench/com/vaadin/tests/resources/NonExistingFileResource.java b/tests/testbench/com/vaadin/tests/resources/NonExistingFileResource.java index e0c7b16318..eb8559f746 100644 --- a/tests/testbench/com/vaadin/tests/resources/NonExistingFileResource.java +++ b/tests/testbench/com/vaadin/tests/resources/NonExistingFileResource.java @@ -25,8 +25,7 @@ public class NonExistingFileResource extends TestBase { @Override public void buttonClick(ClickEvent event) { FileResource res = new FileResource(new File(getContext() - .getBaseDirectory() + "/" + filename), - NonExistingFileResource.this); + .getBaseDirectory() + "/" + filename)); getMainWindow().open(res); } diff --git a/tests/testbench/com/vaadin/tests/resources/ResourceDownload.java b/tests/testbench/com/vaadin/tests/resources/ResourceDownload.java index 3b4e4b9ff3..e323f5285b 100644 --- a/tests/testbench/com/vaadin/tests/resources/ResourceDownload.java +++ b/tests/testbench/com/vaadin/tests/resources/ResourceDownload.java @@ -74,7 +74,7 @@ public class ResourceDownload extends TestBase { return null; } } - }, filename + ".xls", this); + }, filename + ".xls"); streamResource.setCacheTime(5000); // no cache (<=0) does not work with // IE8 streamResource.setMIMEType("application/x-msexcel"); diff --git a/tests/testbench/com/vaadin/tests/tickets/Ticket1737.java b/tests/testbench/com/vaadin/tests/tickets/Ticket1737.java index 44d4109203..caf44865f9 100644 --- a/tests/testbench/com/vaadin/tests/tickets/Ticket1737.java +++ b/tests/testbench/com/vaadin/tests/tickets/Ticket1737.java @@ -12,8 +12,7 @@ import com.vaadin.ui.VerticalLayout; public class Ticket1737 extends Application.LegacyApplication { - Resource slowRes = new ClassResource(Ticket1737.class, "m-bullet-blue.gif", - this) { + Resource slowRes = new ClassResource(Ticket1737.class, "m-bullet-blue.gif") { @Override public DownloadStream getStream() { try { diff --git a/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateConnector.java b/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateConnector.java index fa31267fd4..93b12dbc68 100644 --- a/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateConnector.java +++ b/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateConnector.java @@ -19,7 +19,6 @@ package com.vaadin.tests.widgetset.client.minitutorials.v7a2; import com.google.gwt.user.client.ui.Image; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; -import com.vaadin.shared.communication.URLReference; import com.vaadin.shared.ui.Connect; import com.vaadin.tests.minitutorials.v7a2.ResourceInStateComponent; @@ -28,10 +27,10 @@ public class ResourceInStateConnector extends AbstractComponentConnector { @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); + String icon = getResourceUrl(ResourceInStateState.MY_ICON_RESOURCE); - URLReference icon = getState().getMyIcon(); if (icon != null) { - getWidget().setUrl(icon.getURL()); + getWidget().setUrl(icon); } else { getWidget().setUrl(""); } diff --git a/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateState.java b/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateState.java index 69b415625c..728f0ba5e1 100644 --- a/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateState.java +++ b/tests/testbench/com/vaadin/tests/widgetset/client/minitutorials/v7a2/ResourceInStateState.java @@ -1,17 +1,7 @@ package com.vaadin.tests.widgetset.client.minitutorials.v7a2; import com.vaadin.shared.ComponentState; -import com.vaadin.shared.communication.URLReference; public class ResourceInStateState extends ComponentState { - - private URLReference myIcon; - - public URLReference getMyIcon() { - return myIcon; - } - - public void setMyIcon(URLReference icon) { - myIcon = icon; - } + public static final String MY_ICON_RESOURCE = "myIcon"; }
\ No newline at end of file |