aboutsummaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
authorLeif Åstrand <leif@vaadin.com>2012-08-30 11:30:39 +0300
committerLeif Åstrand <leif@vaadin.com>2012-08-30 12:47:35 +0300
commitfd3826abf42f69db546eff18d269e462843feb49 (patch)
tree2852b77e657e90061842bc2d99dad09b36cd4150 /server/src
parente71794ee177e309a4cba15ebd6b65d5950721dd7 (diff)
downloadvaadin-framework-fd3826abf42f69db546eff18d269e462843feb49.tar.gz
vaadin-framework-fd3826abf42f69db546eff18d269e462843feb49.zip
Replace ApplicationResource with ConnectorResource (#9419)
Diffstat (limited to 'server/src')
-rw-r--r--server/src/com/vaadin/Application.java120
-rw-r--r--server/src/com/vaadin/server/AbstractClientConnector.java60
-rw-r--r--server/src/com/vaadin/server/AbstractCommunicationManager.java6
-rw-r--r--server/src/com/vaadin/server/AbstractWebApplicationContext.java64
-rw-r--r--server/src/com/vaadin/server/ApplicationResource.java85
-rw-r--r--server/src/com/vaadin/server/ApplicationResourceHandler.java62
-rw-r--r--server/src/com/vaadin/server/ClassResource.java109
-rw-r--r--server/src/com/vaadin/server/ClientConnector.java27
-rw-r--r--server/src/com/vaadin/server/ConnectorResource.java43
-rw-r--r--server/src/com/vaadin/server/ConnectorResourceHandler.java81
-rw-r--r--server/src/com/vaadin/server/DragAndDropService.java7
-rw-r--r--server/src/com/vaadin/server/DynamicConnectorResource.java95
-rw-r--r--server/src/com/vaadin/server/FileResource.java61
-rw-r--r--server/src/com/vaadin/server/GlobalResourceHandler.java239
-rw-r--r--server/src/com/vaadin/server/JsonPaintTarget.java7
-rw-r--r--server/src/com/vaadin/server/ResourceReference.java100
-rw-r--r--server/src/com/vaadin/server/StreamResource.java77
-rw-r--r--server/src/com/vaadin/service/ApplicationContext.java56
-rw-r--r--server/src/com/vaadin/ui/AbstractComponent.java6
-rw-r--r--server/src/com/vaadin/ui/AbstractEmbedded.java15
-rw-r--r--server/src/com/vaadin/ui/AbstractMedia.java41
-rw-r--r--server/src/com/vaadin/ui/ConnectorTracker.java14
-rw-r--r--server/src/com/vaadin/ui/Link.java19
-rw-r--r--server/src/com/vaadin/ui/LoginForm.java31
-rw-r--r--server/src/com/vaadin/ui/Video.java6
25 files changed, 876 insertions, 555 deletions
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);
}
}