summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--WebContent/VAADIN/themes/reindeer/window/img/black/close-pressed.png (renamed from WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png)bin359 -> 359 bytes
-rw-r--r--all/build.xml2
-rw-r--r--build/common.xml2
-rwxr-xr-xbuild/ide.xml19
-rw-r--r--buildhelpers/build.xml2
-rw-r--r--client-compiled/build.xml4
-rw-r--r--client-compiler/build.xml2
-rw-r--r--client/build.xml2
-rw-r--r--client/src/com/vaadin/client/communication/AtmospherePushConnection.java9
-rw-r--r--client/src/com/vaadin/client/ui/VFilterSelect.java67
-rw-r--r--client/src/com/vaadin/client/ui/VRichTextArea.java2
-rw-r--r--client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java2
-rw-r--r--common.xml10
-rw-r--r--ivysettings-publish.xml2
-rw-r--r--ivysettings.xml2
-rw-r--r--pom-template.xml2
-rw-r--r--publish.xml6
-rw-r--r--push/build.xml14
-rw-r--r--server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java9
-rw-r--r--server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java4
-rw-r--r--server/src/com/vaadin/server/BootstrapHandler.java11
-rw-r--r--server/src/com/vaadin/server/communication/AtmospherePushConnection.java2
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java90
-rw-r--r--server/src/com/vaadin/util/CurrentInstance.java123
-rw-r--r--server/src/com/vaadin/util/WeakValueMap.java230
-rw-r--r--server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java25
-rw-r--r--server/tests/src/com/vaadin/util/TestCurrentInstance.java53
-rw-r--r--shared/build.xml2
-rw-r--r--shared/src/com/vaadin/shared/ApplicationConstants.java8
-rw-r--r--theme-compiler/build.xml2
-rw-r--r--themes/build.xml4
-rw-r--r--uitest/build.xml5
-rw-r--r--uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java2
-rw-r--r--uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html52
-rw-r--r--uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java93
-rw-r--r--uitest/src/com/vaadin/tests/push/EnableDisablePush.html97
-rw-r--r--uitest/src/com/vaadin/tests/push/EnableDisablePush.java119
-rw-r--r--uitest/src/com/vaadin/tests/push/TablePushStreaming.java126
-rw-r--r--uitest/vaadin-server.xml2
40 files changed, 870 insertions, 339 deletions
diff --git a/.gitignore b/.gitignore
index d5899b3d58..30c8597b16 100644
--- a/.gitignore
+++ b/.gitignore
@@ -45,6 +45,7 @@
/WebContent/VAADIN/gwt-unitCache*
WebContent/VAADIN/vaadinPush.js
+WebContent/VAADIN/vaadinPush.debug.js
# /WebContent/WEB-INF/
/WebContent/WEB-INF/classes
diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png b/WebContent/VAADIN/themes/reindeer/window/img/black/close-pressed.png
index 07a837b619..07a837b619 100644
--- a/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png
+++ b/WebContent/VAADIN/themes/reindeer/window/img/black/close-pressed.png
Binary files differ
diff --git a/all/build.xml b/all/build.xml
index 080346403f..1b48721ecd 100644
--- a/all/build.xml
+++ b/all/build.xml
@@ -122,4 +122,4 @@
<!-- No tests for this zip.. -->
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/build/common.xml b/build/common.xml
index 39f3ee7d9f..9220cf6612 100644
--- a/build/common.xml
+++ b/build/common.xml
@@ -32,7 +32,7 @@
<target name="init-taskdefs" depends="ivy-configure" unless="deps.initialized">
<echo>Loading Ant tasks</echo>
- <ivy:resolve file="${project.root}/build/ivy/ivy.xml" conf="taskdefs" />
+ <ivy:resolve log="download-only" file="${project.root}/build/ivy/ivy.xml" conf="taskdefs" />
<ivy:cachepath pathid="taskdefs.classpath" conf="taskdefs" />
<taskdef resource="emma_ant.properties" classpathref="taskdefs.classpath" />
diff --git a/build/ide.xml b/build/ide.xml
index 45130e9d01..2605bf2cdd 100755
--- a/build/ide.xml
+++ b/build/ide.xml
@@ -13,17 +13,17 @@
<property name="work.dir" location="work" />
<echo>Using gwt files from ${gwt.user.classes} and ${gwt.dev.classes}</echo>
- <ivy:resolve file="client-compiler/ivy.xml" conf="ide" />
+ <ivy:resolve log="download-only" file="client-compiler/ivy.xml" conf="ide" />
<ivy:cachepath pathid="client-compiler.deps" conf="ide" />
- <ivy:resolve file="server/ivy.xml" conf="ide" />
+ <ivy:resolve log="download-only" file="server/ivy.xml" conf="ide" />
<ivy:cachepath pathid="server.deps" conf="ide" />
- <ivy:resolve file="client/ivy.xml" conf="ide" />
+ <ivy:resolve log="download-only" file="client/ivy.xml" conf="ide" />
<ivy:cachepath pathid="client.deps" conf="ide" />
- <ivy:resolve file="shared/ivy.xml" conf="ide" />
+ <ivy:resolve log="download-only" file="shared/ivy.xml" conf="ide" />
<ivy:cachepath pathid="shared.deps" conf="ide" />
- <ivy:resolve file="uitest/ivy.xml" conf="ide" />
+ <ivy:resolve log="download-only" file="uitest/ivy.xml" conf="ide" />
<ivy:cachepath pathid="uitest.deps" conf="ide" />
- <ivy:resolve file="theme-compiler/ivy.xml" conf="ide" />
+ <ivy:resolve log="download-only" file="theme-compiler/ivy.xml" conf="ide" />
<ivy:cachepath pathid="theme-compiler.deps" conf="ide" />
<path id="classpath">
@@ -104,7 +104,7 @@
<echo>Compiling ${module} to ${module.output.dir} with parameters -logLevel TRACE -style ${style} -localWorkers ${localWorkers} -strict ${extraParams}</echo>
- <!--<ivy:resolve inline="true" organisation="javax.validation" module="validation-api"
+ <!--<ivy:resolve log="download-only" inline="true" organisation="javax.validation" module="validation-api"
revision="1.0.0.GA"/> -->
<!-- compile the module -->
@@ -134,6 +134,7 @@
</target>
<target name="vaadinPush.js">
<property name="vaadinPush.js.output" location="WebContent/VAADIN/vaadinPush.js" />
+ <property name="vaadinPush.debug.js.output" location="WebContent/VAADIN/vaadinPush.debug.js" />
<loadfile srcfile="WebContent/VAADIN/jquery-1.7.2.js" property="jquery.js.contents" />
<loadfile srcfile="WebContent/VAADIN/jquery.atmosphere.js" property="jquery.atmosphere.js.contents" />
@@ -146,5 +147,7 @@
</filterchain>
</loadfile>
<echo file="${vaadinPush.js.output}">${vaadinPush.js.contents}</echo>
+ <!-- This vaadinPush.js is not obfuscated so the debug version is the same-->
+ <copy file="${vaadinPush.js.output}" tofile="${vaadinPush.debug.js.output}"/>
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/buildhelpers/build.xml b/buildhelpers/build.xml
index 7d9e23683e..28eb55e6e8 100644
--- a/buildhelpers/build.xml
+++ b/buildhelpers/build.xml
@@ -48,4 +48,4 @@
<!--<antcall target="common.test.run" /> -->
<echo>WHAT? No JUnit tests for ${module.name}!</echo>
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/client-compiled/build.xml b/client-compiled/build.xml
index fa5ff262db..883d3c88ea 100644
--- a/client-compiled/build.xml
+++ b/client-compiled/build.xml
@@ -40,7 +40,7 @@
<target name="compile-module-cache">
<fail unless="module" message="You must give the module to compile in the 'module' parameter" />
- <ivy:resolve resolveid="common" conf="compile-module" />
+ <ivy:resolve log="download-only" resolveid="common" conf="compile-module" />
<ivy:cachepath pathid="classpath.compile.widgetset" conf="compile-module" />
<echo>Creating gwtar files for ${module} in ${gwtar.dir}</echo>
@@ -63,7 +63,7 @@
<property name="localWorkers" value="2" />
<property name="extraParams" value="" />
- <ivy:resolve resolveid="common" conf="compile-module" />
+ <ivy:resolve log="download-only" resolveid="common" conf="compile-module" />
<ivy:cachepath pathid="classpath.compile.widgetset" conf="compile-module" />
<mkdir dir="${module.output.dir}" />
diff --git a/client-compiler/build.xml b/client-compiler/build.xml
index 67c3c318a7..97189fc437 100644
--- a/client-compiler/build.xml
+++ b/client-compiler/build.xml
@@ -66,4 +66,4 @@
<echo>WHAT? No tests for ${module.name}!</echo>
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/client/build.xml b/client/build.xml
index dec8b84a18..19ec05b28a 100644
--- a/client/build.xml
+++ b/client/build.xml
@@ -72,4 +72,4 @@
<antcall target="common.test.run" />
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
index 93d5d879dc..ccb01c5a30 100644
--- a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
+++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java
@@ -22,6 +22,7 @@ import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.user.client.Command;
+import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.CommunicationErrorHandler;
import com.vaadin.client.ResourceLoader;
@@ -489,7 +490,13 @@ public class AtmospherePushConnection implements PushConnection {
if (isAtmosphereLoaded()) {
command.execute();
} else {
- final String pushJs = ApplicationConstants.VAADIN_PUSH_JS;
+ final String pushJs;
+ if (ApplicationConfiguration.isProductionMode()) {
+ pushJs = ApplicationConstants.VAADIN_PUSH_JS;
+ } else {
+ pushJs = ApplicationConstants.VAADIN_PUSH_DEBUG_JS;
+ }
+
VConsole.log("Loading " + pushJs);
ResourceLoader.get().loadScript(
connection.getConfiguration().getVaadinDirUrl() + pushJs,
diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java
index a4041cc34d..7efb5b8867 100644
--- a/client/src/com/vaadin/client/ui/VFilterSelect.java
+++ b/client/src/com/vaadin/client/ui/VFilterSelect.java
@@ -916,6 +916,27 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
}
}
+ /**
+ * TextBox variant used as input element for filter selects, which prevents
+ * selecting text when disabled.
+ *
+ * @since 7.1.5
+ */
+ public class FilterSelectTextBox extends TextBox {
+
+ /**
+ * Overridden to avoid selecting text when text input is disabled
+ */
+ @Override
+ public void setSelectionRange(int pos, int length) {
+ if (textInputEnabled) {
+ super.setSelectionRange(pos, length);
+ } else {
+ super.setSelectionRange(getValue().length(), 0);
+ }
+ }
+ }
+
@Deprecated
public static final FilteringMode FILTERINGMODE_OFF = FilteringMode.OFF;
@Deprecated
@@ -938,21 +959,10 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
* <p>
* For internal use only. May be removed or replaced in the future.
*/
- public final TextBox tb = new TextBox() {
-
- // Overridden to avoid selecting text when text input is disabled
- @Override
- public void setSelectionRange(int pos, int length) {
- if (textInputEnabled) {
- super.setSelectionRange(pos, length);
- } else {
- super.setSelectionRange(getValue().length(), 0);
- }
- };
- };
+ public final TextBox tb;
/** For internal use only. May be removed or replaced in the future. */
- public final SuggestionPopup suggestionPopup = new SuggestionPopup();
+ public final SuggestionPopup suggestionPopup;
/**
* Used when measuring the width of the popup
@@ -1096,9 +1106,12 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
private boolean textInputEnabled = true;
/**
- * Default constructor
+ * Default constructor.
*/
public VFilterSelect() {
+ tb = createTextBox();
+ suggestionPopup = createSuggestionPopup();
+
selectedItemIcon.setStyleName("v-icon");
selectedItemIcon.addLoadHandler(new LoadHandler() {
@@ -1136,6 +1149,32 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler,
setStyleName(CLASSNAME);
}
+ /**
+ * This method will create the TextBox used by the VFilterSelect instance.
+ * It is invoked during the Constructor and should only be overridden if a
+ * custom TextBox shall be used. The overriding method cannot use any
+ * instance variables.
+ *
+ * @since 7.1.5
+ * @return TextBox instance used by this VFilterSelect
+ */
+ protected TextBox createTextBox() {
+ return new FilterSelectTextBox();
+ }
+
+ /**
+ * This method will create the SuggestionPopup used by the VFilterSelect
+ * instance. It is invoked during the Constructor and should only be
+ * overridden if a custom SuggestionPopup shall be used. The overriding
+ * method cannot use any instance variables.
+ *
+ * @since 7.1.5
+ * @return SuggestionPopup instance used by this VFilterSelect
+ */
+ protected SuggestionPopup createSuggestionPopup() {
+ return new SuggestionPopup();
+ }
+
@Override
public void setStyleName(String style) {
super.setStyleName(style);
diff --git a/client/src/com/vaadin/client/ui/VRichTextArea.java b/client/src/com/vaadin/client/ui/VRichTextArea.java
index 34693ce392..0b2c1e574c 100644
--- a/client/src/com/vaadin/client/ui/VRichTextArea.java
+++ b/client/src/com/vaadin/client/ui/VRichTextArea.java
@@ -403,7 +403,7 @@ public class VRichTextArea extends Composite implements Field, KeyPressHandler,
result = "";
}
} else if (browser.isWebkit()) {
- if ("<div><br></div>".equals(result)) {
+ if ("<br>".equals(result) || "<div><br></div>".equals(result)) {
result = "";
}
} else if (browser.isIE()) {
diff --git a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
index 20dfc74c69..d2576eb133 100644
--- a/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
+++ b/client/src/com/vaadin/client/ui/richtextarea/RichTextAreaConnector.java
@@ -104,9 +104,9 @@ public class RichTextAreaConnector extends AbstractFieldConnector implements
if (getConnection() != null && getConnectorId() != null) {
final String html = getWidget().getSanitizedValue();
if (!html.equals(cachedValue)) {
+ cachedValue = html;
getConnection().updateVariable(getConnectorId(), "text", html,
getState().immediate);
- getWidget().setValue(html);
}
}
};
diff --git a/common.xml b/common.xml
index 993802404f..88fbc49196 100644
--- a/common.xml
+++ b/common.xml
@@ -14,7 +14,7 @@
<ivy:settings file="${vaadin.basedir}/ivysettings.xml" />
<ivy:settings file="${vaadin.basedir}/ivysettings.xml" id="ivysettings" />
- <ivy:resolve file="${vaadin.basedir}/ivy-taskdefs.xml" conf="taskdefs" log="quiet" />
+ <ivy:resolve log="download-only" file="${vaadin.basedir}/ivy-taskdefs.xml" conf="taskdefs" />
<ivy:cachepath pathid="taskdefs.classpath" conf="taskdefs" />
<taskdef uri="antlib:net.sf.antcontrib" resource="net/sf/antcontrib/antlib.xml" classpathref="taskdefs.classpath" />
<!-- ant contrib for Maven integration -->
@@ -354,12 +354,12 @@
<target name="dependencies" description="Resolves dependencies needed by this module">
<property name='conf' value="build, build-provided" />
- <ivy:resolve resolveid="common" conf="${conf}" />
+ <ivy:resolve log="download-only" resolveid="common" conf="${conf}" />
<ivy:cachepath pathid="classpath.compile.dependencies" conf="${conf}" />
</target>
<target name="dependencies.test" description="Resolves dependencies needed by test">
- <ivy:resolve resolveid="common" conf="test" />
+ <ivy:resolve log="download-only" resolveid="common" conf="test" />
<ivy:cachepath pathid="classpath.test.dependencies" conf="test" />
</target>
@@ -372,7 +372,7 @@
<fail unless="result.dir" message="No result.dir parameter given" />
<property name="conf" value="*(public)" />
- <ivy:resolve conf="${conf}" />
+ <ivy:resolve log="download-only" conf="${conf}" />
<ivy:publish settingsref="ivysettings" conf="${conf}" resolver="build-temp" overwrite="true" forcedeliver="true">
<!-- <artifacts pattern="${result.dir}/[artifact]-[revision].[ext]"
/> -->
@@ -384,7 +384,7 @@
<target name="publish.to.local.maven">
<property name="conf" value="*(public)" />
- <ivy:resolve conf="${conf}" />
+ <ivy:resolve log="download-only" conf="${conf}" />
<ivy:publish conf="${conf}" resolver="local-maven" overwrite="true">
</ivy:publish>
</target>
diff --git a/ivysettings-publish.xml b/ivysettings-publish.xml
index 4f96b16b72..aeb759d59b 100644
--- a/ivysettings-publish.xml
+++ b/ivysettings-publish.xml
@@ -28,4 +28,4 @@
</resolvers>
-</ivysettings> \ No newline at end of file
+</ivysettings>
diff --git a/ivysettings.xml b/ivysettings.xml
index 3db0a8a06d..fa08f3d0bb 100644
--- a/ivysettings.xml
+++ b/ivysettings.xml
@@ -44,4 +44,4 @@
</modules>
-</ivysettings> \ No newline at end of file
+</ivysettings>
diff --git a/pom-template.xml b/pom-template.xml
index 1716abc82a..2a9c1f0f28 100644
--- a/pom-template.xml
+++ b/pom-template.xml
@@ -96,4 +96,4 @@
</snapshots>
</repository>
</repositories>
-</project> \ No newline at end of file
+</project>
diff --git a/publish.xml b/publish.xml
index 008451a2f8..4007f2db00 100644
--- a/publish.xml
+++ b/publish.xml
@@ -41,7 +41,7 @@
<target name="publish.module.to.download.site">
<fail unless="module" message="No module to publish defined" />
- <ivy:resolve file="${module}/ivy.xml" />
+ <ivy:resolve log="download-only" file="${module}/ivy.xml" />
<ivy:publish publishivy="false" settingsref="publish.settings" conf="*(public)" resolver="sftp-publish">
<artifacts pattern="${ivy.settings.dir}/result/artifacts/[revision]/[module]/[artifact]-[revision](-[classifier]).[ext]" />
</ivy:publish>
@@ -52,7 +52,7 @@
<property file="${gpg.passphrase.file}" />
<!-- Ivy should be able to handle this but this does not work at
- the moment <ivy:resolve file="${module}/ivy.xml" /> <ivy:publish pubrevision="7.0-SNAPSHOT"
+ the moment <ivy:resolve log="download-only" file="${module}/ivy.xml" /> <ivy:publish pubrevision="7.0-SNAPSHOT"
publishivy="false" settingsref="publish.settings" conf="*(public)" resolver="sonatype">
<artifacts pattern="${ivy.settings.dir}/result/artifacts/${vaadin.version}/[module]/[artifact]-${vaadin.version}(-[classifier]).[ext]"
/> </ivy:publish> -->
@@ -70,4 +70,4 @@
</artifact:mvn>
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/push/build.xml b/push/build.xml
index ead3a0226e..1cbe23d75b 100644
--- a/push/build.xml
+++ b/push/build.xml
@@ -11,6 +11,7 @@
<property name="module.symbolic" value="com.vaadin.push" />
<property name="result.dir" location="result" />
<property name="vaadinPush.js" location="${result.dir}/js/VAADIN/vaadinPush.js" />
+ <property name="vaadinPush.debug.js" location="${result.dir}/js/VAADIN/vaadinPush.debug.js" />
<!-- Keep the version number in sync with ivy.xml, server/src/com/vaadin/server/Constants.java and jquery.atmosphere.js -->
<property name="atmosphere.version" value="1.0.14.vaadin4" />
@@ -21,13 +22,12 @@
<union id="jar.includes">
<fileset dir="${result.dir}/js">
<include name="VAADIN/vaadinPush.js" />
+ <include name="VAADIN/vaadinPush.debug.js" />
</fileset>
</union>
<target name="vaadinPush.js">
<mkdir dir="${result.dir}/js/VAADIN" />
- <property name="vaadinPush.js.output" location="${result.dir}/js/VAADIN/vaadinPush.js" />
- <property name="vaadinPush.js.combined.output" location="${result.dir}/js/VAADIN/push.combined.js" />
<loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/jquery-${jquery.version}.js" property="jquery.js.contents" />
<loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/jquery.atmosphere.js" property="jquery.atmosphere.js.contents" />
@@ -39,15 +39,17 @@
</replacetokens>
</filterchain>
</loadfile>
- <echo file="${vaadinPush.js.combined.output}">${vaadinPush.js.contents}</echo>
+
+ <!-- Non-obfuscated version for debugging -->
+ <echo file="${vaadinPush.debug.js}">${vaadinPush.js.contents}</echo>
<!-- Minify -->
<ivy:retrieve organisation="com.yahoo.platform.yui" module="yuicompressor" revision="2.4.7" inline="true" type="jar" pattern="${result.dir}/compressor.jar" />
<java jar="${result.dir}/compressor.jar" fork="true">
<arg value="-v" />
<arg value="-o" />
- <arg file="${vaadinPush.js.output}" />
- <arg file="${vaadinPush.js.combined.output}" />
+ <arg file="${vaadinPush.js}" />
+ <arg file="${vaadinPush.debug.js}" />
</java>
</target>
@@ -70,4 +72,4 @@
<target name="test" depends="checkstyle">
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java b/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java
index cddf2d8a42..7c252d78f2 100644
--- a/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java
+++ b/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java
@@ -44,6 +44,10 @@ public class DateToSqlDateConverter implements Converter<Date, java.sql.Date> {
+ targetType.getName() + ")");
}
+ if (value == null) {
+ return null;
+ }
+
return new java.sql.Date(value.getTime());
}
@@ -56,6 +60,11 @@ public class DateToSqlDateConverter implements Converter<Date, java.sql.Date> {
+ getPresentationType().getName() + " (targetType was "
+ targetType.getName() + ")");
}
+
+ if (value == null) {
+ return null;
+ }
+
return new Date(value.getTime());
}
diff --git a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
index 39c8365076..b54a630e04 100644
--- a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
+++ b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java
@@ -663,7 +663,9 @@ public class TableQuery extends AbstractTransactionalQuery implements
} catch (SQLException ignore) {
} finally {
try {
- tables.close();
+ if (tables != null) {
+ tables.close();
+ }
} catch (SQLException ignore) {
}
}
diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java
index f237d9edd4..5a117958a0 100644
--- a/server/src/com/vaadin/server/BootstrapHandler.java
+++ b/server/src/com/vaadin/server/BootstrapHandler.java
@@ -389,9 +389,16 @@ public abstract class BootstrapHandler extends SynchronizedRequestHandler {
if (context.getPushMode().isEnabled()) {
// Load client-side dependencies for push support
+ String pushJS = vaadinLocation;
+ if (context.getRequest().getService().getDeploymentConfiguration()
+ .isProductionMode()) {
+ pushJS += ApplicationConstants.VAADIN_PUSH_JS;
+ } else {
+ pushJS += ApplicationConstants.VAADIN_PUSH_DEBUG_JS;
+ }
+
fragmentNodes.add(new Element(Tag.valueOf("script"), "").attr(
- "type", "text/javascript").attr("src",
- vaadinLocation + ApplicationConstants.VAADIN_PUSH_JS));
+ "type", "text/javascript").attr("src", pushJS));
}
String bootstrapLocation = vaadinLocation + "vaadinBootstrap.js";
diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
index e325550c4b..b9d4955b12 100644
--- a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
+++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
@@ -221,8 +221,6 @@ public class AtmospherePushConnection implements PushConnection {
}
resource.resume();
- assert !resource.getBroadcaster().getAtmosphereResources()
- .contains(resource);
resource = null;
}
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java
index 1c50f79349..81dd00084d 100644
--- a/server/src/com/vaadin/server/communication/PushHandler.java
+++ b/server/src/com/vaadin/server/communication/PushHandler.java
@@ -173,7 +173,8 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
};
/**
- * Callback used when a connection is closed by the client.
+ * Callback used when a connection is closed, either deliberately or because
+ * an error occurred.
*/
private final PushEventCallback disconnectCallback = new PushEventCallback() {
@Override
@@ -192,8 +193,7 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
if (!pushMode.isEnabled()) {
/*
* The client is expected to close the connection after push
- * mode has been set to disabled, just clean up some stuff
- * and be done with it
+ * mode has been set to disabled.
*/
getLogger().log(Level.FINER,
"Connection closed for resource {0}", id);
@@ -248,24 +248,17 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
} catch (ServiceException e) {
getLogger().log(Level.SEVERE,
"Could not get session. This should never happen", e);
+ return;
} catch (SessionExpiredException e) {
SystemMessages msg = service.getSystemMessages(
ServletPortletHelper.findLocale(null, null,
vaadinRequest), vaadinRequest);
- try {
- resource.getResponse()
- .getWriter()
- .write(VaadinService
- .createCriticalNotificationJSON(
- msg.getSessionExpiredCaption(),
- msg.getSessionExpiredMessage(),
- null, msg.getSessionExpiredURL()));
- } catch (IOException e1) {
- getLogger()
- .log(Level.WARNING,
- "Failed to notify client about unavailable session",
- e);
- }
+ sendNotificationAndDisconnect(
+ resource,
+ VaadinService.createCriticalNotificationJSON(
+ msg.getSessionExpiredCaption(),
+ msg.getSessionExpiredMessage(), null,
+ msg.getSessionExpiredURL()));
return;
}
@@ -275,21 +268,15 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
// Sets UI.currentInstance
final UI ui = service.findUI(vaadinRequest);
if (ui == null) {
- // This a request through an already open push connection to
- // a UI which no longer exists.
- resource.getResponse()
- .getWriter()
- .write(UidlRequestHandler.getUINotFoundErrorJSON(
- service, vaadinRequest));
- // End the connection
- resource.resume();
- return;
+ sendNotificationAndDisconnect(resource,
+ UidlRequestHandler.getUINotFoundErrorJSON(service,
+ vaadinRequest));
+ } else {
+ callback.run(resource, ui);
}
-
- callback.run(resource, ui);
} catch (IOException e) {
- getLogger().log(Level.INFO,
- "An error occured while writing a push response", e);
+ getLogger().log(Level.WARNING, "Error writing a push response",
+ e);
} finally {
session.unlock();
}
@@ -323,9 +310,10 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
AtmosphereResource resource = event.getResource();
String id = resource.uuid();
- if (event.isCancelled()) {
- // Disconnected for whatever reason, handle in onDisconnect() as
- // it's more reliable
+ if (event.isCancelled() || event.isResumedOnTimeout()) {
+ getLogger().log(Level.FINER,
+ "Cancelled connection for resource {0}", id);
+ disconnect(event);
} else if (event.isResuming()) {
// A connection that was suspended earlier was resumed (committed to
// the client.) Should only happen if the transport is JSONP or
@@ -364,13 +352,31 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
public void onDisconnect(AtmosphereResourceEvent event) {
// Log event on trace level
super.onDisconnect(event);
- callWithUi(event.getResource(), disconnectCallback);
+ disconnect(event);
+ }
+
+ @Override
+ public void onThrowable(AtmosphereResourceEvent event) {
+ getLogger().log(Level.SEVERE, "Exception in push connection",
+ event.throwable());
+ disconnect(event);
+ }
+
+ @Override
+ public void onResume(AtmosphereResourceEvent event) {
+ // Log event on trace level
+ super.onResume(event);
+ disconnect(event);
}
@Override
public void destroy() {
}
+ private void disconnect(AtmosphereResourceEvent event) {
+ callWithUi(event.getResource(), disconnectCallback);
+ }
+
/**
* Sends a refresh message to the given atmosphere resource. Uses an
* AtmosphereResource instead of an AtmospherePushConnection even though it
@@ -396,6 +402,22 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter
}
}
+ /**
+ * Tries to send a critical notification to the client and close the
+ * connection. Does nothing if the connection is already closed.
+ */
+ private static void sendNotificationAndDisconnect(
+ AtmosphereResource resource, String notificationJson) {
+ // TODO Implemented differently from sendRefreshAndDisconnect
+ try {
+ resource.getResponse().getWriter().write(notificationJson);
+ resource.resume();
+ } catch (Exception e) {
+ getLogger().log(Level.FINEST,
+ "Failed to send critical notification to client", e);
+ }
+ }
+
private static final Logger getLogger() {
return Logger.getLogger(PushHandler.class.getName());
}
diff --git a/server/src/com/vaadin/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java
index a1c543117d..4c62ef49be 100644
--- a/server/src/com/vaadin/util/CurrentInstance.java
+++ b/server/src/com/vaadin/util/CurrentInstance.java
@@ -17,10 +17,14 @@
package com.vaadin.util;
import java.io.Serializable;
+import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
+import java.util.logging.Level;
+import java.util.logging.Logger;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
@@ -36,6 +40,10 @@ import com.vaadin.ui.UI;
* when {@link VaadinSession#access(Runnable)} or {@link UI#access(Runnable)} is
* used.
* <p>
+ * Please note that the instances are stored using {@link WeakReference}. This
+ * means that the a current instance value may suddenly disappear if there a no
+ * other references to the object.
+ * <p>
* Currently the framework uses the following instances:
* </p>
* <p>
@@ -49,7 +57,9 @@ import com.vaadin.ui.UI;
* @since 7.0.0
*/
public class CurrentInstance implements Serializable {
- private final Object instance;
+ private static final Object NULL_OBJECT = new Object();
+
+ private final WeakReference<Object> instance;
private final boolean inheritable;
private static InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() {
@@ -60,7 +70,7 @@ public class CurrentInstance implements Serializable {
return null;
}
- Map<Class<?>, CurrentInstance> value = new WeakValueMap<Class<?>, CurrentInstance>();
+ Map<Class<?>, CurrentInstance> value = new HashMap<Class<?>, CurrentInstance>();
// Copy all inheritable values to child map
for (Entry<Class<?>, CurrentInstance> e : parentValue.entrySet()) {
@@ -74,7 +84,7 @@ public class CurrentInstance implements Serializable {
};
private CurrentInstance(Object instance, boolean inheritable) {
- this.instance = instance;
+ this.instance = new WeakReference<Object>(instance);
this.inheritable = inheritable;
}
@@ -93,12 +103,49 @@ public class CurrentInstance implements Serializable {
}
CurrentInstance currentInstance = map.get(type);
if (currentInstance != null) {
- return type.cast(currentInstance.instance);
+ Object value = currentInstance.instance.get();
+ if (value == null) {
+ /*
+ * This is believed to never actually happen since the
+ * ThreadLocal should only outlive the referenced object on
+ * threads that are not doing anything related to Vaadin, which
+ * should thus never invoke CurrentInstance.get().
+ *
+ * At this point, there might also be other values that have
+ * been collected, so we'll scan the entire map and remove stale
+ * CurrentInstance objects. Using a ReferenceQueue could make
+ * this assumingly rare case slightly more efficient, but would
+ * significantly increase the complexity of the code for
+ * maintaining a separate ReferenceQueue for each Thread.
+ */
+ removeStaleInstances(map);
+
+ if (map.isEmpty()) {
+ instances.remove();
+ }
+
+ return null;
+ }
+ return type.cast(value);
} else {
return null;
}
}
+ private static void removeStaleInstances(Map<Class<?>, CurrentInstance> map) {
+ for (Iterator<Entry<Class<?>, CurrentInstance>> iterator = map
+ .entrySet().iterator(); iterator.hasNext();) {
+ Entry<Class<?>, CurrentInstance> entry = iterator.next();
+ Object instance = entry.getValue().instance.get();
+ if (instance == null) {
+ iterator.remove();
+ getLogger().log(Level.FINE,
+ "CurrentInstance for {0} has been garbage collected.",
+ entry.getKey());
+ }
+ }
+ }
+
/**
* Sets the current instance of the given type.
*
@@ -183,9 +230,37 @@ public class CurrentInstance implements Serializable {
* A Class -> CurrentInstance map to set as current instances
*/
public static void restoreInstances(Map<Class<?>, CurrentInstance> old) {
+ boolean removeStale = false;
for (Class c : old.keySet()) {
CurrentInstance ci = old.get(c);
- set(c, ci.instance, ci.inheritable);
+ Object v = ci.instance.get();
+ if (v == null) {
+ removeStale = true;
+ } else if (v == NULL_OBJECT) {
+ /*
+ * NULL_OBJECT is used to identify objects that are null when
+ * #setCurrent(UI) or #setCurrent(VaadinSession) are called on a
+ * CurrentInstance. Without this a reference to an already
+ * collected instance may be left in the CurrentInstance when it
+ * really should be restored to null.
+ *
+ * One example case that this fixes:
+ * VaadinService.runPendingAccessTasks() clears all current
+ * instances and then sets everything but the UI. This makes
+ * UI.accessSynchronously() save these values before calling
+ * setCurrent(UI), which stores UI=null in the map it returns.
+ * This map will be restored after UI.accessSync(), which,
+ * unless it respects null values, will just leave the wrong UI
+ * instance registered.
+ */
+ set(c, null, ci.inheritable);
+ } else {
+ set(c, v, ci.inheritable);
+ }
+ }
+
+ if (removeStale) {
+ removeStaleInstances(old);
}
}
@@ -207,12 +282,21 @@ public class CurrentInstance implements Serializable {
return Collections.emptyMap();
} else {
Map<Class<?>, CurrentInstance> copy = new HashMap<Class<?>, CurrentInstance>();
+ boolean removeStale = false;
for (Class<?> c : map.keySet()) {
CurrentInstance ci = map.get(c);
- if (ci.inheritable || !onlyInheritable) {
+ if (ci.instance.get() == null) {
+ removeStale = true;
+ } else if (ci.inheritable || !onlyInheritable) {
copy.put(c, ci);
}
}
+ if (removeStale) {
+ removeStaleInstances(map);
+ if (map.isEmpty()) {
+ instances.remove();
+ }
+ }
return copy;
}
}
@@ -231,7 +315,8 @@ public class CurrentInstance implements Serializable {
*/
public static Map<Class<?>, CurrentInstance> setCurrent(UI ui) {
Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>();
- old.put(UI.class, new CurrentInstance(UI.getCurrent(), true));
+ old.put(UI.class,
+ new CurrentInstance(getSameOrNullObject(UI.getCurrent()), true));
UI.setCurrent(ui);
old.putAll(setCurrent(ui.getSession()));
return old;
@@ -252,10 +337,10 @@ public class CurrentInstance implements Serializable {
public static Map<Class<?>, CurrentInstance> setCurrent(
VaadinSession session) {
Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>();
- old.put(VaadinSession.class,
- new CurrentInstance(VaadinSession.getCurrent(), true));
- old.put(VaadinService.class,
- new CurrentInstance(VaadinService.getCurrent(), true));
+ old.put(VaadinSession.class, new CurrentInstance(
+ getSameOrNullObject(VaadinSession.getCurrent()), true));
+ old.put(VaadinService.class, new CurrentInstance(
+ getSameOrNullObject(VaadinService.getCurrent()), true));
VaadinService service = null;
if (session != null) {
service = session.getService();
@@ -266,4 +351,20 @@ public class CurrentInstance implements Serializable {
return old;
}
+
+ /**
+ * Returns {@code object} unless it is null, in which case #NULL_OBJECT is
+ * returned.
+ *
+ * @param object
+ * The instance to return if non-null.
+ * @return {@code object} or #NULL_OBJECT if {@code object} is null.
+ */
+ private static Object getSameOrNullObject(Object object) {
+ return object == null ? NULL_OBJECT : object;
+ }
+
+ private static Logger getLogger() {
+ return Logger.getLogger(CurrentInstance.class.getName());
+ }
}
diff --git a/server/src/com/vaadin/util/WeakValueMap.java b/server/src/com/vaadin/util/WeakValueMap.java
deleted file mode 100644
index 1134594cba..0000000000
--- a/server/src/com/vaadin/util/WeakValueMap.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * Copyright 2000-2013 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.util;
-
-import java.lang.ref.Reference;
-import java.lang.ref.ReferenceQueue;
-import java.lang.ref.WeakReference;
-import java.util.AbstractMap;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A Map holding weak references to its values. It is internally backed by a
- * normal HashMap and all values are stored as WeakReferences. Garbage collected
- * entries are removed when touched.
- * <p>
- * <em>Note</em> this class is not serializable.
- *
- * @author Vaadin Ltd
- * @since 7.1.4
- */
-public class WeakValueMap<K, V> implements Map<K, V> {
-
- /**
- * This class holds a weak reference to the value and a strong reference to
- * the key for efficient removal of stale values.
- */
- private static class WeakValueReference<K, V> extends WeakReference<V> {
- private final K key;
-
- WeakValueReference(K key, V value, ReferenceQueue<V> refQueue) {
- super(value, refQueue);
- this.key = key;
- }
-
- K getKey() {
- return key;
- }
- }
-
- private final HashMap<K, WeakValueReference<K, V>> backingMap;
- private final ReferenceQueue<V> refQueue;
-
- /**
- * Constructs a new WeakValueMap, where all values are stored as weak
- * references.
- */
- public WeakValueMap() {
- backingMap = new HashMap<K, WeakValueReference<K, V>>();
- refQueue = new ReferenceQueue<V>();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public V put(K key, V value) {
- if (key == null) {
- throw new NullPointerException("key cannot be null");
- }
- if (value == null) {
- throw new NullPointerException("value cannot be null");
- }
- removeStaleEntries();
- backingMap.put(key, new WeakValueReference<K, V>(key, value, refQueue));
- return value;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public V remove(Object o) {
- removeStaleEntries();
- WeakReference<V> value = backingMap.remove(o);
- return value == null ? null : value.get();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void putAll(Map<? extends K, ? extends V> map) {
- if (map != null) {
- for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
- put(entry.getKey(), entry.getValue());
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void clear() {
- backingMap.clear();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Set<K> keySet() {
- removeStaleEntries();
- return backingMap.keySet();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public V get(Object o) {
- removeStaleEntries();
- WeakReference<V> weakValue = backingMap.get(o);
- if (weakValue != null) {
- return weakValue.get();
- }
- return null;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int size() {
- removeStaleEntries();
- return backingMap.size();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isEmpty() {
- removeStaleEntries();
- return backingMap.isEmpty();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean containsKey(Object o) {
- removeStaleEntries();
- return backingMap.containsKey(o);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean containsValue(Object o) {
- removeStaleEntries();
- for (V value : values()) {
- if (o.equals(value)) {
- return true;
- }
- }
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Collection<V> values() {
- removeStaleEntries();
- Collection<V> values = new HashSet<V>();
- for (WeakReference<V> weakValue : backingMap.values()) {
- V value = weakValue.get();
- if (value != null) {
- // null values have been GC'd, which may happen long before
- // anything is enqueued in the ReferenceQueue.
- values.add(value);
- }
- }
- return values;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Set<Entry<K, V>> entrySet() {
- removeStaleEntries();
- Set<Entry<K, V>> entrySet = new HashSet<Entry<K, V>>();
- for (Entry<K, WeakValueReference<K, V>> entry : backingMap.entrySet()) {
- V value = entry.getValue().get();
- if (value != null) {
- // null values have been GC'd, which may happen long before
- // anything is enqueued in the ReferenceQueue.
- entrySet.add(new AbstractMap.SimpleEntry<K, V>(entry.getKey(),
- value));
- }
- }
- return entrySet;
- }
-
- /**
- * Cleans up stale entries by polling the ReferenceQueue.
- * <p>
- * Depending on the GC implementation and strategy, the ReferenceQueue is
- * not necessarily notified immediately when a reference is garbage
- * collected, but it will eventually be.
- */
- private void removeStaleEntries() {
- Reference<? extends V> ref;
- while ((ref = refQueue.poll()) != null) {
- Object key = ((WeakValueReference<?, ?>) ref).getKey();
- backingMap.remove(key);
- }
- }
-}
diff --git a/server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java b/server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java
new file mode 100644
index 0000000000..685404ded6
--- /dev/null
+++ b/server/tests/src/com/vaadin/tests/data/converter/TestDateToSqlDateConverter.java
@@ -0,0 +1,25 @@
+package com.vaadin.tests.data.converter;
+
+import java.util.Date;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+
+import com.vaadin.data.util.converter.DateToSqlDateConverter;
+
+public class TestDateToSqlDateConverter extends TestCase {
+
+ DateToSqlDateConverter converter = new DateToSqlDateConverter();
+
+ public void testNullConversion() {
+ assertEquals(null,
+ converter.convertToModel(null, java.sql.Date.class, null));
+ }
+
+ public void testValueConversion() {
+ Date testDate = new Date(100, 0, 1);
+ long time = testDate.getTime();
+ assertEquals(testDate, converter.convertToModel(new java.sql.Date(time),
+ java.sql.Date.class, Locale.ENGLISH));
+ }
+}
diff --git a/server/tests/src/com/vaadin/util/TestCurrentInstance.java b/server/tests/src/com/vaadin/util/TestCurrentInstance.java
index da986abe31..1910172aa8 100644
--- a/server/tests/src/com/vaadin/util/TestCurrentInstance.java
+++ b/server/tests/src/com/vaadin/util/TestCurrentInstance.java
@@ -15,14 +15,21 @@
*/
package com.vaadin.util;
+import static org.junit.Assert.assertNull;
+
import java.lang.reflect.Field;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
-import junit.framework.Assert;
-
+import org.easymock.EasyMock;
+import org.junit.Assert;
import org.junit.Test;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinService;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.ui.UI;
+
public class TestCurrentInstance {
@Test
@@ -142,4 +149,46 @@ public class TestCurrentInstance {
public void testInheritedClearedAfterRemove() {
}
+
+ private static class UIStoredInCurrentInstance extends UI {
+ @Override
+ protected void init(VaadinRequest request) {
+ }
+ }
+
+ private static class SessionStoredInCurrentInstance extends VaadinSession {
+ public SessionStoredInCurrentInstance(VaadinService service) {
+ super(service);
+ }
+ }
+
+ @Test
+ public void testRestoringNullUIWorks() throws Exception {
+ // First make sure current instance is empty
+ CurrentInstance.clearAll();
+
+ // Then store a new UI in there
+ Map<Class<?>, CurrentInstance> old = CurrentInstance
+ .setCurrent(new UIStoredInCurrentInstance());
+
+ // Restore the old values and assert that the UI is null again
+ CurrentInstance.restoreInstances(old);
+ assertNull(CurrentInstance.get(UI.class));
+ }
+
+ @Test
+ public void testRestoringNullSessionWorks() throws Exception {
+ // First make sure current instance is empty
+ CurrentInstance.clearAll();
+
+ // Then store a new session in there
+ Map<Class<?>, CurrentInstance> old = CurrentInstance
+ .setCurrent(new SessionStoredInCurrentInstance(EasyMock
+ .createNiceMock(VaadinService.class)));
+
+ // Restore the old values and assert that the session is null again
+ CurrentInstance.restoreInstances(old);
+ assertNull(CurrentInstance.get(VaadinSession.class));
+ assertNull(CurrentInstance.get(VaadinService.class));
+ }
}
diff --git a/shared/build.xml b/shared/build.xml
index b0533550d4..73a0a8df7f 100644
--- a/shared/build.xml
+++ b/shared/build.xml
@@ -61,4 +61,4 @@
<echo>WHAT? No tests for ${module.name}!</echo>
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java
index 5ae52615bb..8e66afbb32 100644
--- a/shared/src/com/vaadin/shared/ApplicationConstants.java
+++ b/shared/src/com/vaadin/shared/ApplicationConstants.java
@@ -79,6 +79,14 @@ public class ApplicationConstants implements Serializable {
public static final String VAADIN_PUSH_JS = "vaadinPush.js";
/**
+ * The name of the debug version of the javascript containing push support.
+ * The file is located in the VAADIN directory.
+ *
+ * @since 7.1.6
+ */
+ public static final String VAADIN_PUSH_DEBUG_JS = "vaadinPush.debug.js";
+
+ /**
* Name of the parameter used to transmit the CSRF token.
*/
public static final String CSRF_TOKEN_PARAMETER = "v-csrfToken";
diff --git a/theme-compiler/build.xml b/theme-compiler/build.xml
index 1fd78209c3..9141605fbf 100644
--- a/theme-compiler/build.xml
+++ b/theme-compiler/build.xml
@@ -57,4 +57,4 @@
<antcall target="common.test.run" />
</target>
-</project> \ No newline at end of file
+</project>
diff --git a/themes/build.xml b/themes/build.xml
index 5ee7ec4ec7..93959e054f 100644
--- a/themes/build.xml
+++ b/themes/build.xml
@@ -22,7 +22,7 @@
</union>
<target name="compile-themes">
- <ivy:resolve resolveid="common" conf="build" />
+ <ivy:resolve log="download-only" resolveid="common" conf="build" />
<ivy:cachepath pathid="classpath.compile.theme" conf="build" />
<antcall target="compile-theme">
@@ -69,7 +69,7 @@
<target name="compile-theme" depends="copy-theme">
<fail unless="theme" message="You must give the theme name to compile in the 'theme' parameter" />
- <ivy:resolve resolveid="common" conf="compile-theme" />
+ <ivy:resolve log="download-only" resolveid="common" conf="compile-theme" />
<ivy:cachepath pathid="classpath.compile.theme" conf="compile-theme" />
<ivy:cachepath pathid="classpath.runtime.theme" conf="build" />
diff --git a/uitest/build.xml b/uitest/build.xml
index aaf891925e..bd0f49ae1e 100644
--- a/uitest/build.xml
+++ b/uitest/build.xml
@@ -19,7 +19,7 @@
<!-- This is copied from common.xml to be able to add server.test.source
to the source path -->
- <ivy:resolve resolveid="common" conf="build, build-provided" />
+ <ivy:resolve log="download-only" resolveid="common" conf="build, build-provided" />
<ivy:cachepath pathid="classpath.compile.dependencies" conf="build, build-provided" />
</target>
@@ -97,7 +97,7 @@
<property name="deps.dir" location="${result.dir}/deps" />
<property name="src" location="${result.dir}/../src" />
- <ivy:resolve resolveid="common" conf="build" />
+ <ivy:resolve log="download-only" resolveid="common" conf="build" />
<ivy:cachepath pathid="classpath.runtime.dependencies" conf="build" />
<delete dir="${deps.dir}" />
@@ -120,6 +120,7 @@
<include name="statictestfiles/**" />
<include name="VAADIN/themes/tests-*/**" />
<include name="VAADIN/themes/reindeer-tests/**" />
+ <include name="VAADIN/jquery.atmosphere.js" />
<include name="WEB-INF/*.xml" />
<include name="WEB-INF/web.xml.2.4" />
</fileset>
diff --git a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
index 99f9707479..5ab2134cdb 100644
--- a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
+++ b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java
@@ -51,7 +51,7 @@ import com.vaadin.launcher.util.BrowserLauncher;
*/
public class DevelopmentServerLauncher {
- private static final String KEYSTORE = "src/com/vaadin/launcher/keystore";
+ private static final String KEYSTORE = "uitest/src/com/vaadin/launcher/keystore";
private final static int serverPort = 8888;
/**
diff --git a/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html
new file mode 100644
index 0000000000..fe030f2ea7
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.html
@@ -0,0 +1,52 @@
+<?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.components.ui.CurrentUiRetained?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>pause</td>
+ <td>2000</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_3</td>
+ <td>1. Correct UI.getCurrent before GC: true</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_2</td>
+ <td>2. Correct UI.getCurrent after GC: true</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_1</td>
+ <td>3. GC probe available before GC: true</td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runcomvaadintestscomponentsuiCurrentUiRetained::PID_SLog_row_0</td>
+ <td>4. GC probe available after GC: false</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java
new file mode 100644
index 0000000000..b0127e3a75
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/components/ui/CurrentUiRetained.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2000-2013 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.tests.components.ui;
+
+import java.util.ArrayList;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.tests.components.AbstractTestUIWithLog;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.UI;
+import com.vaadin.util.CurrentInstance;
+
+public class CurrentUiRetained extends AbstractTestUIWithLog {
+ public static class GcProbe {
+
+ }
+
+ @Override
+ protected void setup(VaadinRequest request) {
+ final ArrayList<UI> uiLog = new ArrayList<UI>();
+ final ArrayList<Boolean> probeLog = new ArrayList<Boolean>();
+
+ final Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ uiLog.add(UI.getCurrent());
+
+ GcProbe gcProbe = new GcProbe();
+ CurrentInstance.set(GcProbe.class, gcProbe);
+ probeLog.add(CurrentInstance.get(GcProbe.class) != null);
+ gcProbe = null;
+
+ Thread.sleep(500l);
+ System.gc();
+ Thread.sleep(500l);
+
+ probeLog.add(CurrentInstance.get(GcProbe.class) != null);
+ uiLog.add(UI.getCurrent());
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+ });
+ thread.start();
+
+ addComponent(new Button("Show result", new Button.ClickListener() {
+ @Override
+ public void buttonClick(ClickEvent event) {
+ try {
+ thread.join();
+
+ log("Correct UI.getCurrent before GC: "
+ + (uiLog.get(0) == CurrentUiRetained.this));
+ log("Correct UI.getCurrent after GC: "
+ + (uiLog.get(1) == CurrentUiRetained.this));
+
+ log("GC probe available before GC: " + probeLog.get(0));
+ log("GC probe available after GC: " + probeLog.get(1));
+
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }));
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Tests that garbage collection removes stale CurrentInstance values while retaining values not collected.";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return Integer.valueOf(12509);
+ }
+
+}
diff --git a/uitest/src/com/vaadin/tests/push/EnableDisablePush.html b/uitest/src/com/vaadin/tests/push/EnableDisablePush.html
new file mode 100644
index 0000000000..87dfa8428f
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/push/EnableDisablePush.html
@@ -0,0 +1,97 @@
+<?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>EnableDisablePush</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr><td rowspan="1" colspan="3">EnableDisablePush</td></tr>
+</thead><tbody>
+<tr>
+ <td>open</td>
+ <td>/run/EnableDisablePush?restartApplication</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>1. Push enabled</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>3. Push disabled</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>5. Poll enabled</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>7. Push enabled</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>9. Poll disabled</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>11. Push disabled, polling enabled</td>
+</tr>
+<tr>
+ <td>pause</td>
+ <td>3500</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>16. Polling disabled, push enabled</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>vaadin=runEnableDisablePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td>
+ <td></td>
+</tr>
+<tr>
+ <td>assertText</td>
+ <td>vaadin=runEnableDisablePush::PID_SLog_row_0</td>
+ <td>18. Push disabled</td>
+</tr>
+
+</tbody></table>
+</body>
+</html>
diff --git a/uitest/src/com/vaadin/tests/push/EnableDisablePush.java b/uitest/src/com/vaadin/tests/push/EnableDisablePush.java
new file mode 100644
index 0000000000..1911c66c2d
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/push/EnableDisablePush.java
@@ -0,0 +1,119 @@
+package com.vaadin.tests.push;
+
+import java.util.Date;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.TimeUnit;
+
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.communication.PushMode;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.tests.util.Log;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.UIDetachedException;
+
+public class EnableDisablePush extends AbstractTestUI {
+
+ private int c = 0;
+
+ private Log log = new Log(15);
+
+ private final Timer timer = new Timer(true);
+
+ private final class CounterTask extends TimerTask {
+
+ @Override
+ public void run() {
+
+ try {
+ while (true) {
+ TimeUnit.MILLISECONDS.sleep(1000);
+
+ access(new Runnable() {
+ @Override
+ public void run() {
+ log.log("Counter = " + c++);
+ if (c == 3) {
+ log.log("Disabling polling, enabling push");
+ getPushConfiguration().setPushMode(
+ PushMode.AUTOMATIC);
+ setPollInterval(-1);
+ log.log("Polling disabled, push enabled");
+ }
+ }
+ });
+ }
+ } catch (InterruptedException e) {
+ } catch (UIDetachedException e) {
+ }
+ }
+ };
+
+ @Override
+ protected void setup(VaadinRequest request) {
+
+ getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
+ log.log("Push enabled");
+
+ addComponent(new Button("Disable push", new Button.ClickListener() {
+ @Override
+ public void buttonClick(Button.ClickEvent event) {
+ log.log("Disabling push");
+ getPushConfiguration().setPushMode(PushMode.DISABLED);
+ log.log("Push disabled");
+ }
+ }));
+
+ addComponent(new Button("Enable push", new Button.ClickListener() {
+ @Override
+ public void buttonClick(Button.ClickEvent event) {
+ log.log("Enabling push");
+ getPushConfiguration().setPushMode(PushMode.AUTOMATIC);
+ log.log("Push enabled");
+ }
+ }));
+
+ addComponent(new Button("Disable polling", new Button.ClickListener() {
+ @Override
+ public void buttonClick(Button.ClickEvent event) {
+ log.log("Disabling poll");
+ setPollInterval(-1);
+ log.log("Poll disabled");
+ }
+ }));
+
+ addComponent(new Button("Enable polling", new Button.ClickListener() {
+ @Override
+ public void buttonClick(Button.ClickEvent event) {
+ log.log("Enabling poll");
+ setPollInterval(1000);
+ log.log("Poll enabled");
+ }
+ }));
+
+ addComponent(new Button(
+ "Disable push, re-enable from background thread",
+ new Button.ClickListener() {
+ @Override
+ public void buttonClick(Button.ClickEvent event) {
+ log.log("Disabling push, enabling polling");
+ getPushConfiguration().setPushMode(PushMode.DISABLED);
+ setPollInterval(1000);
+ timer.schedule(new CounterTask(), new Date());
+ log.log("Push disabled, polling enabled");
+ }
+ }));
+
+ addComponent(log);
+ }
+
+ @Override
+ protected String getTestDescription() {
+ return "Test dynamically enablind and disabling push";
+ }
+
+ @Override
+ protected Integer getTicketNumber() {
+ return 12226;
+ }
+}
diff --git a/uitest/src/com/vaadin/tests/push/TablePushStreaming.java b/uitest/src/com/vaadin/tests/push/TablePushStreaming.java
new file mode 100644
index 0000000000..de824eef3a
--- /dev/null
+++ b/uitest/src/com/vaadin/tests/push/TablePushStreaming.java
@@ -0,0 +1,126 @@
+/*
+ * Copyright 2000-2013 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.tests.push;
+
+import com.vaadin.annotations.Push;
+import com.vaadin.data.Container;
+import com.vaadin.data.Item;
+import com.vaadin.data.util.IndexedContainer;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.shared.ui.ui.Transport;
+import com.vaadin.tests.components.AbstractTestUI;
+import com.vaadin.ui.Table;
+
+/**
+ *
+ * @since
+ * @author Vaadin Ltd
+ */
+@Push(transport = Transport.STREAMING)
+public class TablePushStreaming extends AbstractTestUI {
+
+ private int iteration = 1;
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#setup(com.vaadin.server.
+ * VaadinRequest)
+ */
+ @Override
+ protected void setup(VaadinRequest request) {
+ final Table t = new Table("The table");
+ t.setContainerDataSource(generateContainer(10, 10, iteration++));
+ t.setSizeFull();
+ Runnable r = new Runnable() {
+
+ @Override
+ public void run() {
+ for (int i = 0; i < 99; i++) {
+ try {
+ Thread.sleep(200);
+ } catch (InterruptedException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ access(new Runnable() {
+
+ @Override
+ public void run() {
+ t.setContainerDataSource(generateContainer(
+ t.getVisibleColumns().length, t.size(),
+ iteration++));
+ }
+
+ });
+ }
+
+ }
+ };
+ Thread tr = new Thread(r);
+ tr.start();
+
+ setContent(t);
+ }
+
+ /**
+ * @param iter
+ * @since
+ * @return
+ */
+ private Container generateContainer(int rows, int cols, int iter) {
+ IndexedContainer ic = new IndexedContainer();
+ for (int col = 1; col <= cols; col++) {
+ ic.addContainerProperty("Property" + col, String.class, "");
+ }
+
+ for (int row = 0; row < rows; row++) {
+ Item item = ic.addItem("row" + row);
+ for (int col = 1; col <= cols; col++) {
+ item.getItemProperty("Property" + col).setValue(
+ "Row " + row + " col " + col + "(" + iter + ")");
+ }
+
+ }
+
+ return ic;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#getTestDescription()
+ */
+ @Override
+ protected String getTestDescription() {
+ return "Test that pushes Table data at a high pace to detect possible problems in the streaming protocol";
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.tests.components.AbstractTestUI#getTicketNumber()
+ */
+ @Override
+ protected Integer getTicketNumber() {
+ return null;
+ }
+
+}
diff --git a/uitest/vaadin-server.xml b/uitest/vaadin-server.xml
index f2c3c60959..4e84a6f238 100644
--- a/uitest/vaadin-server.xml
+++ b/uitest/vaadin-server.xml
@@ -6,7 +6,7 @@
<target name="deploy-and-start">
<fail unless="war.file" message="No war file given in 'war.file'" />
- <ivy:resolve file="${dir}/ivy.xml" />
+ <ivy:resolve log="download-only" file="${dir}/ivy.xml" />
<ivy:cachepath pathid="classpath.jetty" conf="jetty-run" />
<java classname="org.mortbay.jetty.runner.Runner" fork="yes" output="${vaadin.basedir}/result/jetty.java.out" resultproperty="resultCode">
<arg value="--port" />