summaryrefslogtreecommitdiffstats
path: root/server/src
diff options
context:
space:
mode:
Diffstat (limited to 'server/src')
-rw-r--r--server/src/com/vaadin/annotations/Push.java10
-rw-r--r--server/src/com/vaadin/annotations/VaadinServletConfiguration.java143
-rw-r--r--server/src/com/vaadin/data/util/LegacyPropertyHelper.java18
-rw-r--r--server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java120
-rw-r--r--server/src/com/vaadin/data/util/converter/Converter.java6
-rw-r--r--server/src/com/vaadin/data/util/converter/ConverterUtil.java50
-rw-r--r--server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java59
-rw-r--r--server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java2
-rw-r--r--server/src/com/vaadin/data/util/converter/StringToDoubleConverter.java67
-rw-r--r--server/src/com/vaadin/data/util/converter/StringToFloatConverter.java67
-rw-r--r--server/src/com/vaadin/data/util/converter/StringToIntegerConverter.java54
-rw-r--r--server/src/com/vaadin/data/util/converter/StringToNumberConverter.java69
-rw-r--r--server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java2
-rw-r--r--server/src/com/vaadin/navigator/Navigator.java13
-rw-r--r--server/src/com/vaadin/server/AbstractClientConnector.java14
-rw-r--r--server/src/com/vaadin/server/ClientConnector.java26
-rw-r--r--server/src/com/vaadin/server/ComponentSizeValidator.java48
-rw-r--r--server/src/com/vaadin/server/ConnectorResourceHandler.java6
-rw-r--r--server/src/com/vaadin/server/Constants.java7
-rw-r--r--server/src/com/vaadin/server/DefaultDeploymentConfiguration.java56
-rw-r--r--server/src/com/vaadin/server/DragAndDropService.java10
-rw-r--r--server/src/com/vaadin/server/GlobalResourceHandler.java8
-rw-r--r--server/src/com/vaadin/server/JsonPaintTarget.java4
-rw-r--r--server/src/com/vaadin/server/LegacyApplication.java2
-rw-r--r--server/src/com/vaadin/server/LegacyCommunicationManager.java61
-rw-r--r--server/src/com/vaadin/server/LocaleService.java211
-rw-r--r--server/src/com/vaadin/server/Page.java111
-rw-r--r--server/src/com/vaadin/server/RequestHandler.java3
-rw-r--r--server/src/com/vaadin/server/UIProvider.java24
-rw-r--r--server/src/com/vaadin/server/VaadinPortlet.java35
-rw-r--r--server/src/com/vaadin/server/VaadinService.java114
-rw-r--r--server/src/com/vaadin/server/VaadinServlet.java83
-rw-r--r--server/src/com/vaadin/server/VaadinSession.java191
-rw-r--r--server/src/com/vaadin/server/communication/AtmospherePushConnection.java15
-rw-r--r--server/src/com/vaadin/server/communication/FileUploadHandler.java2
-rw-r--r--server/src/com/vaadin/server/communication/LocaleWriter.java204
-rw-r--r--server/src/com/vaadin/server/communication/MetadataWriter.java54
-rw-r--r--server/src/com/vaadin/server/communication/PushConnection.java4
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java11
-rw-r--r--server/src/com/vaadin/server/communication/PushRequestHandler.java24
-rw-r--r--server/src/com/vaadin/server/communication/UIInitHandler.java10
-rw-r--r--server/src/com/vaadin/server/communication/UidlRequestHandler.java87
-rw-r--r--server/src/com/vaadin/server/communication/UidlWriter.java19
-rw-r--r--server/src/com/vaadin/ui/AbstractComponent.java9
-rw-r--r--server/src/com/vaadin/ui/AbstractField.java15
-rw-r--r--server/src/com/vaadin/ui/Calendar.java31
-rw-r--r--server/src/com/vaadin/ui/Label.java9
-rw-r--r--server/src/com/vaadin/ui/LegacyWindow.java2
-rw-r--r--server/src/com/vaadin/ui/LoginForm.java2
-rw-r--r--server/src/com/vaadin/ui/ProgressBar.java152
-rw-r--r--server/src/com/vaadin/ui/ProgressIndicator.java105
-rw-r--r--server/src/com/vaadin/ui/PushConfiguration.java282
-rw-r--r--server/src/com/vaadin/ui/UI.java238
-rw-r--r--server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java2
-rw-r--r--server/src/com/vaadin/util/ConnectorHelper.java101
-rw-r--r--server/src/com/vaadin/util/CurrentInstance.java104
56 files changed, 2098 insertions, 1078 deletions
diff --git a/server/src/com/vaadin/annotations/Push.java b/server/src/com/vaadin/annotations/Push.java
index 58e70acf21..d5e42d6f60 100644
--- a/server/src/com/vaadin/annotations/Push.java
+++ b/server/src/com/vaadin/annotations/Push.java
@@ -22,6 +22,7 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import com.vaadin.shared.communication.PushMode;
+import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.ui.UI;
/**
@@ -46,4 +47,13 @@ public @interface Push {
*/
public PushMode value() default PushMode.AUTOMATIC;
+ /**
+ * Returns the transport type used for the push for the annotated UI. The
+ * default transport type when this annotation is present is
+ * {@link Transport#WEBSOCKET}.
+ *
+ * @return the transport type to use
+ */
+ public Transport transport() default Transport.DEFAULT;
+
}
diff --git a/server/src/com/vaadin/annotations/VaadinServletConfiguration.java b/server/src/com/vaadin/annotations/VaadinServletConfiguration.java
new file mode 100644
index 0000000000..38e3ff2ef0
--- /dev/null
+++ b/server/src/com/vaadin/annotations/VaadinServletConfiguration.java
@@ -0,0 +1,143 @@
+/*
+ * 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.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.vaadin.server.Constants;
+import com.vaadin.server.DefaultDeploymentConfiguration;
+import com.vaadin.server.DeploymentConfiguration;
+import com.vaadin.server.DeploymentConfiguration.LegacyProperyToStringMode;
+import com.vaadin.server.VaadinServlet;
+import com.vaadin.server.VaadinSession;
+import com.vaadin.ui.UI;
+
+/**
+ * Annotation for configuring subclasses of {@link VaadinServlet}. For a
+ * {@link VaadinServlet} class that has this annotation, the defined values are
+ * read during initialization and will be available using
+ * {@link DeploymentConfiguration#getApplicationOrSystemProperty(String, String)}
+ * as well as from specific methods in {@link DeploymentConfiguration}. Init
+ * params defined in <code>web.xml</code> or the <code>@WebServlet</code>
+ * annotation take precedence over values defined in this annotation.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+public @interface VaadinServletConfiguration {
+ /**
+ * Defines the init parameter name for methods in
+ * {@link VaadinServletConfiguration}.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.METHOD)
+ @Documented
+ public @interface InitParameterName {
+ /**
+ * The name of the init parameter that the annotated method controls.
+ *
+ * @return the parameter name
+ */
+ public String value();
+ }
+
+ /**
+ * Whether Vaadin is in production mode.
+ *
+ * @return true if in production mode, false otherwise.
+ *
+ * @see DeploymentConfiguration#isProductionMode()
+ */
+ @InitParameterName(Constants.SERVLET_PARAMETER_PRODUCTION_MODE)
+ public boolean productionMode();
+
+ /**
+ * Gets the default UI class to use for the servlet.
+ *
+ * @return the default UI class
+ */
+ @InitParameterName(VaadinSession.UI_PARAMETER)
+ public Class<? extends UI> ui();
+
+ /**
+ * The time resources can be cached in the browser, in seconds. The default
+ * value is
+ * {@value DefaultDeploymentConfiguration#DEFAULT_RESOURCE_CACHE_TIME}.
+ *
+ * @return the resource cache time
+ *
+ * @see DeploymentConfiguration#getResourceCacheTime()
+ */
+ @InitParameterName(Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME)
+ public int resourceCacheTime() default DefaultDeploymentConfiguration.DEFAULT_RESOURCE_CACHE_TIME;
+
+ /**
+ * The number of seconds between heartbeat requests of a UI, or a
+ * non-positive number if heartbeat is disabled. The default value is
+ * {@value DefaultDeploymentConfiguration#DEFAULT_HEARTBEAT_INTERVAL}.
+ *
+ * @return the time between heartbeats
+ *
+ * @see DeploymentConfiguration#getHeartbeatInterval()
+ */
+ @InitParameterName(Constants.SERVLET_PARAMETER_HEARTBEAT_INTERVAL)
+ public int heartbeatInterval() default DefaultDeploymentConfiguration.DEFAULT_HEARTBEAT_INTERVAL;
+
+ /**
+ * Whether a session should be closed when all its open UIs have been idle
+ * for longer than its configured maximum inactivity time. The default value
+ * is {@value DefaultDeploymentConfiguration#DEFAULT_CLOSE_IDLE_SESSIONS}.
+ *
+ * @return true if UIs and sessions receiving only heartbeat requests are
+ * eventually closed; false if heartbeat requests extend UI and
+ * session lifetime indefinitely
+ *
+ * @see DeploymentConfiguration#isCloseIdleSessions()
+ */
+ @InitParameterName(Constants.SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS)
+ public boolean closeIdleSessions() default DefaultDeploymentConfiguration.DEFAULT_CLOSE_IDLE_SESSIONS;
+
+ /**
+ * The default widgetset to use for the servlet. The default value is
+ * {@value VaadinServlet#DEFAULT_WIDGETSET}.
+ *
+ * @return the default widgetset name
+ */
+ @InitParameterName(VaadinServlet.PARAMETER_WIDGETSET)
+ public String widgetset() default VaadinServlet.DEFAULT_WIDGETSET;
+
+ /**
+ * The legacy Property.toString() mode used. The default value is
+ * {@link LegacyProperyToStringMode#DISABLED}
+ *
+ * @return The Property.toString() mode in use.
+ *
+ * @deprecated as of 7.1, should only be used to ease migration
+ */
+ @Deprecated
+ @InitParameterName(Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING)
+ public LegacyProperyToStringMode legacyPropertyToStringMode() default LegacyProperyToStringMode.DISABLED;
+}
diff --git a/server/src/com/vaadin/data/util/LegacyPropertyHelper.java b/server/src/com/vaadin/data/util/LegacyPropertyHelper.java
index 0276e35dbf..76bd57117d 100644
--- a/server/src/com/vaadin/data/util/LegacyPropertyHelper.java
+++ b/server/src/com/vaadin/data/util/LegacyPropertyHelper.java
@@ -15,6 +15,7 @@
*/
package com.vaadin.data.util;
+import java.io.Serializable;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -32,7 +33,7 @@ import com.vaadin.server.VaadinService;
* @deprecated This is only used internally for backwards compatibility
*/
@Deprecated
-public class LegacyPropertyHelper {
+public class LegacyPropertyHelper implements Serializable {
/**
* Returns the property value converted to a String.
@@ -59,6 +60,11 @@ public class LegacyPropertyHelper {
getLogger().log(Level.WARNING,
Constants.WARNING_LEGACY_PROPERTY_TOSTRING,
p.getClass().getName());
+ if (getLogger().isLoggable(Level.FINE)) {
+ getLogger().log(Level.FINE,
+ "Strack trace for legacy toString to ease debugging",
+ new Throwable());
+ }
}
/**
@@ -76,8 +82,8 @@ public class LegacyPropertyHelper {
*/
public static boolean isLegacyToStringEnabled() {
if (VaadinService.getCurrent() == null) {
- // This should really not happen but we need to handle it somehow.
- // IF it happens it seems more safe to use the legacy mode and log.
+ // This will happen at least in JUnit tests. We do not what the real
+ // value should be but it seems more safe to use the legacy mode.
return true;
}
return VaadinService.getCurrent().getDeploymentConfiguration()
@@ -86,9 +92,9 @@ public class LegacyPropertyHelper {
private static boolean logLegacyToStringWarning() {
if (VaadinService.getCurrent() == null) {
- // This should really not happen but we need to handle it somehow.
- // IF it happens it seems more safe to use the legacy mode and log.
- return true;
+ // This will happen at least in JUnit tests. We do not want to spam
+ // the log with these messages in this case.
+ return false;
}
return VaadinService.getCurrent().getDeploymentConfiguration()
.getLegacyPropertyToStringMode() == LegacyProperyToStringMode.WARNING;
diff --git a/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java b/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java
new file mode 100644
index 0000000000..5999d850b4
--- /dev/null
+++ b/server/src/com/vaadin/data/util/converter/AbstractStringToNumberConverter.java
@@ -0,0 +1,120 @@
+/*
+ * 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.data.util.converter;
+
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Locale;
+
+/**
+ * A converter that converts from the number type T to {@link String} and back.
+ * Uses the given locale and {@link NumberFormat} for formatting and parsing.
+ * Automatically trims the input string, removing any leading and trailing white
+ * space.
+ * <p>
+ * Override and overwrite {@link #getFormat(Locale)} to use a different format.
+ * </p>
+ *
+ * @author Vaadin Ltd
+ * @since 7.1
+ */
+public abstract class AbstractStringToNumberConverter<T> implements
+ Converter<String, T> {
+
+ /**
+ * Returns the format used by {@link #convertToPresentation(Object, Locale)}
+ * and {@link #convertToModel(Object, Locale)}.
+ *
+ * @param locale
+ * The locale to use
+ * @return A NumberFormat instance
+ * @since 7.1
+ */
+ protected NumberFormat getFormat(Locale locale) {
+ if (locale == null) {
+ locale = Locale.getDefault();
+ }
+
+ return NumberFormat.getNumberInstance(locale);
+ }
+
+ /**
+ * Convert the value to a Number using the given locale and
+ * {@link #getFormat(Locale)}.
+ *
+ * @param value
+ * The value to convert
+ * @param locale
+ * The locale to use for conversion
+ * @return The converted value
+ * @throws ConversionException
+ * If there was a problem converting the value
+ * @since 7.1
+ */
+ protected Number convertToNumber(String value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ // Remove leading and trailing white space
+ value = value.trim();
+
+ // Parse and detect errors. If the full string was not used, it is
+ // an error.
+ ParsePosition parsePosition = new ParsePosition(0);
+ Number parsedValue = getFormat(locale).parse(value, parsePosition);
+ if (parsePosition.getIndex() != value.length()) {
+ throw new ConversionException("Could not convert '" + value
+ + "' to " + getModelType().getName());
+ }
+
+ if (parsedValue == null) {
+ // Convert "" to null
+ return null;
+ }
+ return parsedValue;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
+ * .Object, java.util.Locale)
+ */
+ @Override
+ public String convertToPresentation(T value, Locale locale)
+ throws ConversionException {
+ if (value == null) {
+ return null;
+ }
+
+ return getFormat(locale).format(value);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getPresentationType()
+ */
+ @Override
+ public Class<String> getPresentationType() {
+ return String.class;
+ }
+
+}
diff --git a/server/src/com/vaadin/data/util/converter/Converter.java b/server/src/com/vaadin/data/util/converter/Converter.java
index be9bb32413..ded7da7fb5 100644
--- a/server/src/com/vaadin/data/util/converter/Converter.java
+++ b/server/src/com/vaadin/data/util/converter/Converter.java
@@ -38,12 +38,12 @@ import java.util.Locale;
* If conversion of a value fails, a {@link ConversionException} is thrown.
* </p>
*
- * @param <MODEL>
- * The model type. Must be compatible with what
- * {@link #getModelType()} returns.
* @param <PRESENTATION>
* The presentation type. Must be compatible with what
* {@link #getPresentationType()} returns.
+ * @param <MODEL>
+ * The model type. Must be compatible with what
+ * {@link #getModelType()} returns.
* @author Vaadin Ltd.
* @since 7.0
*/
diff --git a/server/src/com/vaadin/data/util/converter/ConverterUtil.java b/server/src/com/vaadin/data/util/converter/ConverterUtil.java
index 61d155bc9a..08d7363084 100644
--- a/server/src/com/vaadin/data/util/converter/ConverterUtil.java
+++ b/server/src/com/vaadin/data/util/converter/ConverterUtil.java
@@ -151,10 +151,14 @@ public class ConverterUtil implements Serializable {
/**
* Checks if the given converter can handle conversion between the given
- * presentation and model type
+ * presentation and model type. Does strict type checking and only returns
+ * true if the converter claims it can handle exactly the given types.
+ *
+ * @see #canConverterPossiblyHandle(Converter, Class, Class)
*
* @param converter
- * The converter to check
+ * The converter to check. If this is null the result is always
+ * false.
* @param presentationType
* The presentation type
* @param modelType
@@ -168,10 +172,48 @@ public class ConverterUtil implements Serializable {
return false;
}
- if (!modelType.isAssignableFrom(converter.getModelType())) {
+ if (modelType != converter.getModelType()) {
+ return false;
+ }
+ if (presentationType != converter.getPresentationType()) {
return false;
}
- if (!presentationType.isAssignableFrom(converter.getPresentationType())) {
+
+ return true;
+ }
+
+ /**
+ * Checks if it possible that the given converter can handle conversion
+ * between the given presentation and model type somehow.
+ *
+ * @param converter
+ * The converter to check. If this is null the result is always
+ * false.
+ * @param presentationType
+ * The presentation type
+ * @param modelType
+ * The model type
+ * @return true if the converter possibly support conversion between the
+ * given presentation and model type, false otherwise
+ */
+ public static boolean canConverterPossiblyHandle(Converter<?, ?> converter,
+ Class<?> presentationType, Class<?> modelType) {
+ if (converter == null) {
+ return false;
+ }
+ Class<?> converterModelType = converter.getModelType();
+
+ if (!modelType.isAssignableFrom(converterModelType)
+ && !converterModelType.isAssignableFrom(modelType)) {
+ // model types are not compatible in any way
+ return false;
+ }
+
+ Class<?> converterPresentationType = converter.getPresentationType();
+ if (!presentationType.isAssignableFrom(converterPresentationType)
+ && !converterPresentationType
+ .isAssignableFrom(presentationType)) {
+ // presentation types are not compatible in any way
return false;
}
diff --git a/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java b/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java
new file mode 100644
index 0000000000..97027cc05b
--- /dev/null
+++ b/server/src/com/vaadin/data/util/converter/DateToSqlDateConverter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.data.util.converter;
+
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * Converter for handling conversion between {@link java.util.Date} and
+ * {@link java.sql.Date}. This is used when a PopupDateField or InlineDateField
+ * is connected to a java.sql.Date property, typically through a JPAContainer or
+ * SQLContainer. Note that information (time information) is lost when
+ * converting from {@link java.util.Date} to {@link java.sql.Date}.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+public class DateToSqlDateConverter implements Converter<Date, java.sql.Date> {
+
+ @Override
+ public java.sql.Date convertToModel(Date value, Locale locale)
+ throws ConversionException {
+ return new java.sql.Date(value.getTime());
+ }
+
+ @Override
+ public Date convertToPresentation(java.sql.Date value, Locale locale)
+ throws ConversionException {
+ return new Date(value.getTime());
+ }
+
+ @Override
+ public Class<java.sql.Date> getModelType() {
+ return java.sql.Date.class;
+ }
+
+ @Override
+ public Class<Date> getPresentationType() {
+ return Date.class;
+ }
+
+}
diff --git a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
index de183dd342..bbd3945a37 100644
--- a/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
+++ b/server/src/com/vaadin/data/util/converter/DefaultConverterFactory.java
@@ -87,6 +87,8 @@ public class DefaultConverterFactory implements ConverterFactory {
protected Converter<Date, ?> createDateConverter(Class<?> sourceType) {
if (Long.class.isAssignableFrom(sourceType)) {
return new DateToLongConverter();
+ } else if (java.sql.Date.class.isAssignableFrom(sourceType)) {
+ return new DateToSqlDateConverter();
} else {
return null;
}
diff --git a/server/src/com/vaadin/data/util/converter/StringToDoubleConverter.java b/server/src/com/vaadin/data/util/converter/StringToDoubleConverter.java
index 69a0faf8f4..8bb82498b9 100644
--- a/server/src/com/vaadin/data/util/converter/StringToDoubleConverter.java
+++ b/server/src/com/vaadin/data/util/converter/StringToDoubleConverter.java
@@ -17,7 +17,6 @@
package com.vaadin.data.util.converter;
import java.text.NumberFormat;
-import java.text.ParsePosition;
import java.util.Locale;
/**
@@ -34,23 +33,8 @@ import java.util.Locale;
* @author Vaadin Ltd
* @since 7.0
*/
-public class StringToDoubleConverter implements Converter<String, Double> {
-
- /**
- * Returns the format used by {@link #convertToPresentation(Double, Locale)}
- * and {@link #convertToModel(String, Locale)}.
- *
- * @param locale
- * The locale to use
- * @return A NumberFormat instance
- */
- protected NumberFormat getFormat(Locale locale) {
- if (locale == null) {
- locale = Locale.getDefault();
- }
-
- return NumberFormat.getNumberInstance(locale);
- }
+public class StringToDoubleConverter extends
+ AbstractStringToNumberConverter<Double> {
/*
* (non-Javadoc)
@@ -62,42 +46,8 @@ public class StringToDoubleConverter implements Converter<String, Double> {
@Override
public Double convertToModel(String value, Locale locale)
throws ConversionException {
- if (value == null) {
- return null;
- }
-
- // Remove leading and trailing white space
- value = value.trim();
-
- ParsePosition parsePosition = new ParsePosition(0);
- Number parsedValue = getFormat(locale).parse(value, parsePosition);
- if (parsePosition.getIndex() != value.length()) {
- throw new ConversionException("Could not convert '" + value
- + "' to " + getModelType().getName());
- }
- if (parsedValue == null) {
- // Convert "" to null
- return null;
- }
-
- return parsedValue.doubleValue();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
- * .Object, java.util.Locale)
- */
- @Override
- public String convertToPresentation(Double value, Locale locale)
- throws ConversionException {
- if (value == null) {
- return null;
- }
-
- return getFormat(locale).format(value);
+ Number n = convertToNumber(value, locale);
+ return n == null ? null : n.doubleValue();
}
/*
@@ -110,13 +60,4 @@ public class StringToDoubleConverter implements Converter<String, Double> {
return Double.class;
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.data.util.converter.Converter#getPresentationType()
- */
- @Override
- public Class<String> getPresentationType() {
- return String.class;
- }
}
diff --git a/server/src/com/vaadin/data/util/converter/StringToFloatConverter.java b/server/src/com/vaadin/data/util/converter/StringToFloatConverter.java
index 1adfd87565..a207654358 100644
--- a/server/src/com/vaadin/data/util/converter/StringToFloatConverter.java
+++ b/server/src/com/vaadin/data/util/converter/StringToFloatConverter.java
@@ -17,7 +17,6 @@
package com.vaadin.data.util.converter;
import java.text.NumberFormat;
-import java.text.ParsePosition;
import java.util.Locale;
/**
@@ -34,23 +33,8 @@ import java.util.Locale;
* @author Vaadin Ltd
* @since 7.0
*/
-public class StringToFloatConverter implements Converter<String, Float> {
-
- /**
- * Returns the format used by {@link #convertToPresentation(Float, Locale)}
- * and {@link #convertToModel(String, Locale)}.
- *
- * @param locale
- * The locale to use
- * @return A NumberFormat instance
- */
- protected NumberFormat getFormat(Locale locale) {
- if (locale == null) {
- locale = Locale.getDefault();
- }
-
- return NumberFormat.getNumberInstance(locale);
- }
+public class StringToFloatConverter extends
+ AbstractStringToNumberConverter<Float> {
/*
* (non-Javadoc)
@@ -62,42 +46,8 @@ public class StringToFloatConverter implements Converter<String, Float> {
@Override
public Float convertToModel(String value, Locale locale)
throws ConversionException {
- if (value == null) {
- return null;
- }
-
- // Remove leading and trailing white space
- value = value.trim();
-
- ParsePosition parsePosition = new ParsePosition(0);
- Number parsedValue = getFormat(locale).parse(value, parsePosition);
- if (parsePosition.getIndex() != value.length()) {
- throw new ConversionException("Could not convert '" + value
- + "' to " + getModelType().getName());
- }
- if (parsedValue == null) {
- // Convert "" to null
- return null;
- }
-
- return parsedValue.floatValue();
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
- * .Object, java.util.Locale)
- */
- @Override
- public String convertToPresentation(Float value, Locale locale)
- throws ConversionException {
- if (value == null) {
- return null;
- }
-
- return getFormat(locale).format(value);
+ Number n = convertToNumber(value, locale);
+ return n == null ? null : n.floatValue();
}
/*
@@ -110,13 +60,4 @@ public class StringToFloatConverter implements Converter<String, Float> {
return Float.class;
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.data.util.converter.Converter#getPresentationType()
- */
- @Override
- public Class<String> getPresentationType() {
- return String.class;
- }
}
diff --git a/server/src/com/vaadin/data/util/converter/StringToIntegerConverter.java b/server/src/com/vaadin/data/util/converter/StringToIntegerConverter.java
index 4bb933bcc8..4f34cf1cd3 100644
--- a/server/src/com/vaadin/data/util/converter/StringToIntegerConverter.java
+++ b/server/src/com/vaadin/data/util/converter/StringToIntegerConverter.java
@@ -17,7 +17,6 @@
package com.vaadin.data.util.converter;
import java.text.NumberFormat;
-import java.text.ParsePosition;
import java.util.Locale;
/**
@@ -31,7 +30,8 @@ import java.util.Locale;
* @author Vaadin Ltd
* @since 7.0
*/
-public class StringToIntegerConverter implements Converter<String, Integer> {
+public class StringToIntegerConverter extends
+ AbstractStringToNumberConverter<Integer> {
/**
* Returns the format used by
@@ -42,6 +42,7 @@ public class StringToIntegerConverter implements Converter<String, Integer> {
* The locale to use
* @return A NumberFormat instance
*/
+ @Override
protected NumberFormat getFormat(Locale locale) {
if (locale == null) {
locale = Locale.getDefault();
@@ -49,50 +50,29 @@ public class StringToIntegerConverter implements Converter<String, Integer> {
return NumberFormat.getIntegerInstance(locale);
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.data.util.converter.Converter#convertToModel(java.lang.Object,
+ * java.util.Locale)
+ */
@Override
public Integer convertToModel(String value, Locale locale)
throws ConversionException {
- if (value == null) {
- return null;
- }
-
- // Remove leading and trailing white space
- value = value.trim();
+ Number n = convertToNumber(value, locale);
+ return n == null ? null : n.intValue();
- // Parse and detect errors. If the full string was not used, it is
- // an error.
- ParsePosition parsePosition = new ParsePosition(0);
- Number parsedValue = getFormat(locale).parse(value, parsePosition);
- if (parsePosition.getIndex() != value.length()) {
- throw new ConversionException("Could not convert '" + value
- + "' to " + getModelType().getName());
- }
-
- if (parsedValue == null) {
- // Convert "" to null
- return null;
- }
- return parsedValue.intValue();
- }
-
- @Override
- public String convertToPresentation(Integer value, Locale locale)
- throws ConversionException {
- if (value == null) {
- return null;
- }
-
- return getFormat(locale).format(value);
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.data.util.converter.Converter#getModelType()
+ */
@Override
public Class<Integer> getModelType() {
return Integer.class;
}
- @Override
- public Class<String> getPresentationType() {
- return String.class;
- }
-
}
diff --git a/server/src/com/vaadin/data/util/converter/StringToNumberConverter.java b/server/src/com/vaadin/data/util/converter/StringToNumberConverter.java
index 99ff7007ad..eae73e4cfa 100644
--- a/server/src/com/vaadin/data/util/converter/StringToNumberConverter.java
+++ b/server/src/com/vaadin/data/util/converter/StringToNumberConverter.java
@@ -17,7 +17,6 @@
package com.vaadin.data.util.converter;
import java.text.NumberFormat;
-import java.text.ParsePosition;
import java.util.Locale;
/**
@@ -30,23 +29,8 @@ import java.util.Locale;
* @author Vaadin Ltd
* @since 7.0
*/
-public class StringToNumberConverter implements Converter<String, Number> {
-
- /**
- * Returns the format used by {@link #convertToPresentation(Number, Locale)}
- * and {@link #convertToModel(String, Locale)}.
- *
- * @param locale
- * The locale to use
- * @return A NumberFormat instance
- */
- protected NumberFormat getFormat(Locale locale) {
- if (locale == null) {
- locale = Locale.getDefault();
- }
-
- return NumberFormat.getNumberInstance(locale);
- }
+public class StringToNumberConverter extends
+ AbstractStringToNumberConverter<Number> {
/*
* (non-Javadoc)
@@ -58,44 +42,7 @@ public class StringToNumberConverter implements Converter<String, Number> {
@Override
public Number convertToModel(String value, Locale locale)
throws ConversionException {
- if (value == null) {
- return null;
- }
-
- // Remove leading and trailing white space
- value = value.trim();
-
- // Parse and detect errors. If the full string was not used, it is
- // an error.
- ParsePosition parsePosition = new ParsePosition(0);
- Number parsedValue = getFormat(locale).parse(value, parsePosition);
- if (parsePosition.getIndex() != value.length()) {
- throw new ConversionException("Could not convert '" + value
- + "' to " + getModelType().getName());
- }
-
- if (parsedValue == null) {
- // Convert "" to null
- return null;
- }
- return parsedValue;
- }
-
- /*
- * (non-Javadoc)
- *
- * @see
- * com.vaadin.data.util.converter.Converter#convertToPresentation(java.lang
- * .Object, java.util.Locale)
- */
- @Override
- public String convertToPresentation(Number value, Locale locale)
- throws ConversionException {
- if (value == null) {
- return null;
- }
-
- return getFormat(locale).format(value);
+ return convertToNumber(value, locale);
}
/*
@@ -108,14 +55,4 @@ public class StringToNumberConverter implements Converter<String, Number> {
return Number.class;
}
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.data.util.converter.Converter#getPresentationType()
- */
- @Override
- public Class<String> getPresentationType() {
- return String.class;
- }
-
}
diff --git a/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
index 65c1050fb0..0cc2f0a1a5 100644
--- a/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
+++ b/server/src/com/vaadin/event/dd/acceptcriteria/SourceIs.java
@@ -48,7 +48,7 @@ public class SourceIs extends ClientSideCriterion {
int paintedComponents = 0;
for (int i = 0; i < components.length; i++) {
Component c = components[i];
- if (c.getUI() != null && c.getUI().getSession() != null) {
+ if (c.isAttached()) {
target.addAttribute("component" + paintedComponents++, c);
} else {
Logger.getLogger(SourceIs.class.getName())
diff --git a/server/src/com/vaadin/navigator/Navigator.java b/server/src/com/vaadin/navigator/Navigator.java
index df05a9fbce..540a3ee302 100644
--- a/server/src/com/vaadin/navigator/Navigator.java
+++ b/server/src/com/vaadin/navigator/Navigator.java
@@ -497,19 +497,22 @@ public class Navigator implements Serializable {
*/
public void navigateTo(String navigationState) {
String longestViewName = null;
+ ViewProvider longestViewNameProvider = null;
View viewWithLongestName = null;
for (ViewProvider provider : providers) {
String viewName = provider.getViewName(navigationState);
if (null != viewName
&& (longestViewName == null || viewName.length() > longestViewName
.length())) {
- View view = provider.getView(viewName);
- if (null != view) {
- longestViewName = viewName;
- viewWithLongestName = view;
- }
+ longestViewName = viewName;
+ longestViewNameProvider = provider;
}
}
+ if (longestViewName != null) {
+ viewWithLongestName = longestViewNameProvider
+ .getView(longestViewName);
+ }
+
if (viewWithLongestName == null && errorProvider != null) {
longestViewName = errorProvider.getViewName(navigationState);
viewWithLongestName = errorProvider.getView(longestViewName);
diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java
index e998b8ed55..01f7d9af42 100644
--- a/server/src/com/vaadin/server/AbstractClientConnector.java
+++ b/server/src/com/vaadin/server/AbstractClientConnector.java
@@ -577,7 +577,7 @@ public abstract class AbstractClientConnector implements ClientConnector,
}
// Send detach event if the component have been connected to a window
- if (getSession() != null) {
+ if (isAttached()) {
detach();
}
@@ -585,7 +585,7 @@ public abstract class AbstractClientConnector implements ClientConnector,
this.parent = parent;
// Send attach event if connected to an application
- if (getSession() != null) {
+ if (isAttached()) {
attach();
}
}
@@ -595,6 +595,16 @@ public abstract class AbstractClientConnector implements ClientConnector,
return parent;
}
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.server.ClientConnector#isAttached()
+ */
+ @Override
+ public boolean isAttached() {
+ return getSession() != null;
+ }
+
@Override
public void attach() {
markAsDirty();
diff --git a/server/src/com/vaadin/server/ClientConnector.java b/server/src/com/vaadin/server/ClientConnector.java
index 3b52fbc730..9e328bb6ef 100644
--- a/server/src/com/vaadin/server/ClientConnector.java
+++ b/server/src/com/vaadin/server/ClientConnector.java
@@ -226,14 +226,22 @@ public interface ClientConnector extends Connector {
public void setParent(ClientConnector parent);
/**
- * Notifies the connector that it is connected to an application.
+ * Checks if the connector is attached to a VaadinSession.
*
+ * @since 7.1
+ * @return true if the connector is attached to a session, false otherwise
+ */
+ public boolean isAttached();
+
+ /**
+ * Notifies the connector that it is connected to a VaadinSession (and
+ * therefore also to a UI).
* <p>
* The caller of this method is {@link #setParent(ClientConnector)} if the
- * parent is itself already attached to the application. If not, the parent
- * will call the {@link #attach()} for all its children when it is attached
- * to the application. This method is always called before the connector's
- * data is sent to the client-side for the first time.
+ * parent is itself already attached to the session. If not, the parent will
+ * call the {@link #attach()} for all its children when it is attached to
+ * the session. This method is always called before the connector's data is
+ * sent to the client-side for the first time.
* </p>
*
* <p>
@@ -243,13 +251,13 @@ public interface ClientConnector extends Connector {
public void attach();
/**
- * Notifies the connector that it is detached from the application.
+ * Notifies the connector that it is detached from its VaadinSession.
*
* <p>
* The caller of this method is {@link #setParent(ClientConnector)} if the
- * parent is in the application. When the parent is detached from the
- * application it is its responsibility to call {@link #detach()} for each
- * of its children.
+ * parent is in the session. When the parent is detached from the session it
+ * is its responsibility to call {@link #detach()} for each of its children.
+ *
* </p>
*/
public void detach();
diff --git a/server/src/com/vaadin/server/ComponentSizeValidator.java b/server/src/com/vaadin/server/ComponentSizeValidator.java
index 27d087a2b2..07c195a1c1 100644
--- a/server/src/com/vaadin/server/ComponentSizeValidator.java
+++ b/server/src/com/vaadin/server/ComponentSizeValidator.java
@@ -16,8 +16,8 @@
package com.vaadin.server;
import java.io.PrintStream;
-import java.io.PrintWriter;
import java.io.Serializable;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
@@ -40,6 +40,7 @@ import com.vaadin.ui.GridLayout.Area;
import com.vaadin.ui.Layout;
import com.vaadin.ui.Panel;
import com.vaadin.ui.TabSheet;
+import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.vaadin.ui.Window;
@@ -190,14 +191,14 @@ public class ComponentSizeValidator implements Serializable {
subErrors.add(error);
}
- public void reportErrors(PrintWriter clientJSON,
+ public void reportErrors(StringBuilder clientJSON,
PrintStream serverErrorStream) {
- clientJSON.write("{");
+ clientJSON.append("{");
Component parent = component.getParent();
String paintableId = component.getConnectorId();
- clientJSON.print("id:\"" + paintableId + "\"");
+ clientJSON.append("\"id\":\"" + paintableId + "\"");
if (invalidHeight) {
Stack<ComponentInfo> attributes = null;
@@ -227,7 +228,7 @@ public class ComponentSizeValidator implements Serializable {
attributes = getHeightAttributes(component);
}
printServerError(msg, attributes, false, serverErrorStream);
- clientJSON.print(",\"heightMsg\":\"" + msg + "\"");
+ clientJSON.append(",\"heightMsg\":\"" + msg + "\"");
}
if (invalidWidth) {
Stack<ComponentInfo> attributes = null;
@@ -255,25 +256,25 @@ public class ComponentSizeValidator implements Serializable {
msg = "A component with relative width needs a parent with defined width.";
attributes = getWidthAttributes(component);
}
- clientJSON.print(",\"widthMsg\":\"" + msg + "\"");
+ clientJSON.append(",\"widthMsg\":\"" + msg + "\"");
printServerError(msg, attributes, true, serverErrorStream);
}
if (subErrors.size() > 0) {
serverErrorStream.println("Sub errors >>");
- clientJSON.write(", \"subErrors\" : [");
+ clientJSON.append(", \"subErrors\" : [");
boolean first = true;
for (InvalidLayout subError : subErrors) {
if (!first) {
- clientJSON.print(",");
+ clientJSON.append(",");
} else {
first = false;
}
subError.reportErrors(clientJSON, serverErrorStream);
}
- clientJSON.write("]");
+ clientJSON.append("]");
serverErrorStream.println("<< Sub erros");
}
- clientJSON.write("}");
+ clientJSON.append("}");
}
}
@@ -673,4 +674,31 @@ public class ComponentSizeValidator implements Serializable {
return Logger.getLogger(ComponentSizeValidator.class.getName());
}
+ /**
+ * Validates the layout and returns a collection of errors
+ *
+ * @since 7.1
+ * @param ui
+ * The UI to validate
+ * @return A collection of errors. An empty collection if there are no
+ * errors.
+ */
+ public static List<InvalidLayout> validateLayouts(UI ui) {
+ List<InvalidLayout> invalidRelativeSizes = ComponentSizeValidator
+ .validateComponentRelativeSizes(ui.getContent(),
+ new ArrayList<ComponentSizeValidator.InvalidLayout>(),
+ null);
+
+ // Also check any existing subwindows
+ if (ui.getWindows() != null) {
+ for (Window subWindow : ui.getWindows()) {
+ invalidRelativeSizes = ComponentSizeValidator
+ .validateComponentRelativeSizes(subWindow.getContent(),
+ invalidRelativeSizes, null);
+ }
+ }
+ return invalidRelativeSizes;
+
+ }
+
}
diff --git a/server/src/com/vaadin/server/ConnectorResourceHandler.java b/server/src/com/vaadin/server/ConnectorResourceHandler.java
index 00d82988d3..3f3f41a179 100644
--- a/server/src/com/vaadin/server/ConnectorResourceHandler.java
+++ b/server/src/com/vaadin/server/ConnectorResourceHandler.java
@@ -77,8 +77,8 @@ public class ConnectorResourceHandler implements RequestHandler {
session.unlock();
}
- Map<Class<?>, CurrentInstance> oldThreadLocals = CurrentInstance
- .setThreadLocals(ui);
+ Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
+ .setCurrent(ui);
try {
if (!connector.handleConnectorRequest(request, response, key)) {
return error(request, response, connector.getClass()
@@ -88,7 +88,7 @@ public class ConnectorResourceHandler implements RequestHandler {
+ ") did not handle connector request for " + key);
}
} finally {
- CurrentInstance.restoreThreadLocals(oldThreadLocals);
+ CurrentInstance.restoreInstances(oldInstances);
}
return true;
diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java
index f8d8105286..2c041e3cf8 100644
--- a/server/src/com/vaadin/server/Constants.java
+++ b/server/src/com/vaadin/server/Constants.java
@@ -65,11 +65,11 @@ public interface Constants {
+ " Widgetset version: %s\n"
+ "=================================================================";
- static final String REQUIRED_ATMOSPHERE_VERSION = "1.0.12";
+ static final String REQUIRED_ATMOSPHERE_VERSION = "1.0.13";
static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n"
+ "=================================================================\n"
- + "Vaadin depends on Atomsphere {0} but version {1} was found.\n"
+ + "Vaadin depends on Atmosphere {0} but version {1} was found.\n"
+ "This might cause compatibility problems if push is used.\n"
+ "=================================================================";
@@ -106,7 +106,8 @@ public interface Constants {
+ "\" to \"true\". To disable the legacy functionality, set \""
+ Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING
+ "\" to false."
- + " (Note that your debugger might call toString() and trigger this message).";
+ + " (Note that your debugger might call toString() and trigger this message)."
+ + " To find out who is calling toString(), enable FINE level logging.";
static final String WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE = "Unknown value '{0}' for parameter "
+ Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING
diff --git a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java
index 80c3644d77..a55c3231f3 100644
--- a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java
+++ b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java
@@ -30,6 +30,27 @@ import com.vaadin.shared.communication.PushMode;
* @since 7.0.0
*/
public class DefaultDeploymentConfiguration implements DeploymentConfiguration {
+ /**
+ * Default value for {@link #getResourceCacheTime()} = {@value} .
+ */
+ public static final int DEFAULT_RESOURCE_CACHE_TIME = 3600;
+
+ /**
+ * Default value for {@link #getHeartbeatInterval()} = {@value} .
+ */
+ public static final int DEFAULT_HEARTBEAT_INTERVAL = 300;
+
+ /**
+ * Default value for {@link #isCloseIdleSessions()} = {@value} .
+ */
+ public static final boolean DEFAULT_CLOSE_IDLE_SESSIONS = false;
+
+ /**
+ * Default value for {@link #getLegacyPropertyToStringMode()} =
+ * {@link LegacyProperyToStringMode#WARNING}.
+ */
+ public static final LegacyProperyToStringMode DEFAULT_LEGACY_PROPERTY_TO_STRING = LegacyProperyToStringMode.WARNING;
+
private final Properties initParameters;
private boolean productionMode;
private boolean xsrfProtectionEnabled;
@@ -66,20 +87,17 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration {
private void checkLegacyPropertyToString() {
String param = getApplicationOrSystemProperty(
- Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING, "warning");
- if ("true".equals(param)) {
- legacyPropertyToStringMode = LegacyProperyToStringMode.ENABLED;
- } else if ("false".equals(param)) {
- legacyPropertyToStringMode = LegacyProperyToStringMode.DISABLED;
- } else {
- if (!"warning".equals(param)) {
- getLogger()
- .log(Level.WARNING,
- Constants.WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE,
- param);
- }
- legacyPropertyToStringMode = LegacyProperyToStringMode.WARNING;
+ Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING,
+ DEFAULT_LEGACY_PROPERTY_TO_STRING.name().toLowerCase());
+ try {
+ legacyPropertyToStringMode = LegacyProperyToStringMode
+ .valueOf(param.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ getLogger().log(Level.WARNING,
+ Constants.WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE,
+ param);
+ legacyPropertyToStringMode = DEFAULT_LEGACY_PROPERTY_TO_STRING;
}
}
@@ -250,11 +268,11 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration {
resourceCacheTime = Integer
.parseInt(getApplicationOrSystemProperty(
Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME,
- "3600"));
+ Integer.toString(DEFAULT_RESOURCE_CACHE_TIME)));
} catch (NumberFormatException e) {
getLogger().warning(
Constants.WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC);
- resourceCacheTime = 3600;
+ resourceCacheTime = DEFAULT_RESOURCE_CACHE_TIME;
}
}
@@ -263,18 +281,18 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration {
heartbeatInterval = Integer
.parseInt(getApplicationOrSystemProperty(
Constants.SERVLET_PARAMETER_HEARTBEAT_INTERVAL,
- "300"));
+ Integer.toString(DEFAULT_HEARTBEAT_INTERVAL)));
} catch (NumberFormatException e) {
getLogger().warning(
Constants.WARNING_HEARTBEAT_INTERVAL_NOT_NUMERIC);
- heartbeatInterval = 300;
+ heartbeatInterval = DEFAULT_HEARTBEAT_INTERVAL;
}
}
private void checkCloseIdleSessions() {
closeIdleSessions = getApplicationOrSystemProperty(
- Constants.SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS, "false")
- .equals("true");
+ Constants.SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS,
+ Boolean.toString(DEFAULT_CLOSE_IDLE_SESSIONS)).equals("true");
}
private void checkPushMode() {
diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java
index a83e83ef7f..cc571853fe 100644
--- a/server/src/com/vaadin/server/DragAndDropService.java
+++ b/server/src/com/vaadin/server/DragAndDropService.java
@@ -375,4 +375,14 @@ public class DragAndDropService implements VariableOwner, ClientConnector {
@Override
public void removeDetachListener(DetachListener listener) {
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.server.ClientConnector#isAttached()
+ */
+ @Override
+ public boolean isAttached() {
+ return true;
+ }
}
diff --git a/server/src/com/vaadin/server/GlobalResourceHandler.java b/server/src/com/vaadin/server/GlobalResourceHandler.java
index d411b286d0..4235d85024 100644
--- a/server/src/com/vaadin/server/GlobalResourceHandler.java
+++ b/server/src/com/vaadin/server/GlobalResourceHandler.java
@@ -87,14 +87,14 @@ public class GlobalResourceHandler implements RequestHandler {
+ " is not a valid global resource path");
}
session.lock();
- Map<Class<?>, CurrentInstance> oldThreadLocals = null;
+ Map<Class<?>, CurrentInstance> oldInstances = null;
DownloadStream stream = null;
try {
UI ui = session.getUIById(Integer.parseInt(uiid));
if (ui == null) {
return error(request, response, "No UI found for id " + uiid);
}
- oldThreadLocals = CurrentInstance.setThreadLocals(ui);
+ oldInstances = CurrentInstance.setCurrent(ui);
ConnectorResource resource;
if (LEGACY_TYPE.equals(type)) {
resource = legacyResources.get(key);
@@ -115,8 +115,8 @@ public class GlobalResourceHandler implements RequestHandler {
}
} finally {
session.unlock();
- if (oldThreadLocals != null) {
- CurrentInstance.restoreThreadLocals(oldThreadLocals);
+ if (oldInstances != null) {
+ CurrentInstance.restoreInstances(oldInstances);
}
}
diff --git a/server/src/com/vaadin/server/JsonPaintTarget.java b/server/src/com/vaadin/server/JsonPaintTarget.java
index ca70391f64..cd09b2a44b 100644
--- a/server/src/com/vaadin/server/JsonPaintTarget.java
+++ b/server/src/com/vaadin/server/JsonPaintTarget.java
@@ -388,10 +388,6 @@ public class JsonPaintTarget implements PaintTarget {
getUsedResources().add("layouts/" + value + ".html");
}
- if (name.equals("locale")) {
- manager.requireLocale(value);
- }
-
}
@Override
diff --git a/server/src/com/vaadin/server/LegacyApplication.java b/server/src/com/vaadin/server/LegacyApplication.java
index 54550ce134..44649067c5 100644
--- a/server/src/com/vaadin/server/LegacyApplication.java
+++ b/server/src/com/vaadin/server/LegacyApplication.java
@@ -64,7 +64,7 @@ public abstract class LegacyApplication implements ErrorHandler {
if (this.mainWindow != null) {
throw new IllegalStateException("mainWindow has already been set");
}
- if (mainWindow.getSession() != null) {
+ if (mainWindow.isAttached()) {
throw new IllegalStateException(
"mainWindow is attached to another application");
}
diff --git a/server/src/com/vaadin/server/LegacyCommunicationManager.java b/server/src/com/vaadin/server/LegacyCommunicationManager.java
index c0194db243..ad662cf6df 100644
--- a/server/src/com/vaadin/server/LegacyCommunicationManager.java
+++ b/server/src/com/vaadin/server/LegacyCommunicationManager.java
@@ -16,18 +16,12 @@
package com.vaadin.server;
-import java.io.IOException;
-import java.io.PrintWriter;
import java.io.Serializable;
-import java.io.Writer;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.GeneralSecurityException;
-import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
@@ -37,7 +31,6 @@ import org.json.JSONException;
import org.json.JSONObject;
import com.vaadin.server.ClientConnector.ConnectorErrorEvent;
-import com.vaadin.server.communication.LocaleWriter;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.JavaScriptConnectorState;
import com.vaadin.shared.communication.SharedState;
@@ -69,9 +62,6 @@ public class LegacyCommunicationManager implements Serializable {
*/
private final VaadinSession session;
- // TODO Refactor to UI shared state (#11378)
- private List<String> locales;
-
// TODO Move to VaadinSession (#11409)
private DragAndDropService dragAndDropService;
@@ -88,7 +78,6 @@ public class LegacyCommunicationManager implements Serializable {
*/
public LegacyCommunicationManager(VaadinSession session) {
this.session = session;
- requireLocale(session.getLocale().toString());
}
protected VaadinSession getSession() {
@@ -313,52 +302,6 @@ public class LegacyCommunicationManager implements Serializable {
}
/**
- * Prints the queued (pending) locale definitions to a {@link PrintWriter}
- * in a (UIDL) format that can be sent to the client and used there in
- * formatting dates, times etc.
- *
- * @deprecated As of 7.1. See #11378.
- *
- * @param outWriter
- */
- @Deprecated
- public void printLocaleDeclarations(Writer writer) throws IOException {
- new LocaleWriter().write(locales, writer);
- }
-
- /**
- * Queues a locale to be sent to the client (browser) for date and time
- * entry etc. All locale specific information is derived from server-side
- * {@link Locale} instances and sent to the client when needed, eliminating
- * the need to use the {@link Locale} class and all the framework behind it
- * on the client.
- *
- * @deprecated As of 7.1. See #11378.
- *
- * @see Locale#toString()
- *
- * @param value
- */
- @Deprecated
- public void requireLocale(String value) {
- if (locales == null) {
- locales = new ArrayList<String>();
- locales.add(session.getLocale().toString());
- }
- if (!locales.contains(value)) {
- locales.add(value);
- }
- }
-
- /**
- * @deprecated As of 7.1. See #11378.
- */
- @Deprecated
- public void resetLocales() {
- locales = null;
- }
-
- /**
* @deprecated As of 7.1. Will be removed in the future.
*/
@Deprecated
@@ -486,10 +429,6 @@ public class LegacyCommunicationManager implements Serializable {
getClientCache(ui).clear();
ui.getConnectorTracker().markAllConnectorsDirty();
ui.getConnectorTracker().markAllClientSidesUninitialized();
-
- // Reset sent locales
- resetLocales();
- requireLocale(session.getLocale().toString());
}
private static final Logger getLogger() {
diff --git a/server/src/com/vaadin/server/LocaleService.java b/server/src/com/vaadin/server/LocaleService.java
new file mode 100644
index 0000000000..347c4da5c6
--- /dev/null
+++ b/server/src/com/vaadin/server/LocaleService.java
@@ -0,0 +1,211 @@
+/*
+ * 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.server;
+
+import java.text.DateFormat;
+import java.text.DateFormatSymbols;
+import java.text.SimpleDateFormat;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.logging.Logger;
+
+import com.vaadin.shared.ui.ui.UIState.LocaleData;
+import com.vaadin.shared.ui.ui.UIState.LocaleServiceState;
+import com.vaadin.ui.UI;
+
+/**
+ * Server side service which handles locale and the transmission of locale date
+ * to the client side LocaleService.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+public class LocaleService {
+
+ private UI ui;
+
+ private LocaleServiceState state;
+
+ /**
+ * Creates a LocaleService bound to the given UI
+ *
+ * @since 7.1
+ * @param ui
+ * The UI which owns the LocaleService
+ */
+ public LocaleService(UI ui, LocaleServiceState state) {
+ this.ui = ui;
+ this.state = state;
+ }
+
+ /**
+ * Retrieves the UI this service is bound to
+ *
+ * @since 7.1
+ * @return the UI for this service
+ */
+ public UI getUI() {
+ return ui;
+ }
+
+ /**
+ * Adds a locale to be sent to the client (browser) for date and time entry
+ * etc. All locale specific information is derived from server-side
+ * {@link Locale} instances and sent to the client when needed, eliminating
+ * the need to use the {@link Locale} class and all the framework behind it
+ * on the client.
+ *
+ * @param locale
+ * The locale which is required on the client side
+ */
+ public void addLocale(Locale locale) {
+ for (LocaleData data : getState(false).localeData) {
+ if (data.name.equals(locale.toString())) {
+ // Already there
+ return;
+ }
+ }
+
+ getState(true).localeData.add(createLocaleData(locale));
+ }
+
+ /**
+ * Returns the state for this service
+ * <p>
+ * The state is transmitted inside the UI state rather than as an individual
+ * entity.
+ * </p>
+ *
+ * @since 7.1
+ * @param markAsDirty
+ * true to mark the state as dirty
+ * @return a LocaleServiceState object that can be read in any case and
+ * modified if markAsDirty is true
+ */
+ private LocaleServiceState getState(boolean markAsDirty) {
+ if (markAsDirty) {
+ getUI().markAsDirty();
+ }
+
+ return state;
+ }
+
+ /**
+ * Creates a LocaleData instance for transportation to the client
+ *
+ * @since 7.1
+ * @param locale
+ * The locale for which to create a LocaleData object
+ * @return A LocaleData object with information about the given locale
+ */
+ protected LocaleData createLocaleData(Locale locale) {
+ LocaleData localeData = new LocaleData();
+ localeData.name = locale.toString();
+
+ final DateFormatSymbols dfs = new DateFormatSymbols(locale);
+ localeData.shortMonthNames = dfs.getShortMonths();
+ localeData.monthNames = dfs.getMonths();
+ // Client expects 0 based indexing, DateFormatSymbols use 1 based
+ localeData.shortDayNames = new String[7];
+ localeData.dayNames = new String[7];
+ String[] sDayNames = dfs.getShortWeekdays();
+ String[] lDayNames = dfs.getWeekdays();
+ for (int i = 0; i < 7; i++) {
+ localeData.shortDayNames[i] = sDayNames[i + 1];
+ localeData.dayNames[i] = lDayNames[i + 1];
+ }
+
+ /*
+ * First day of week (0 = sunday, 1 = monday)
+ */
+ final java.util.Calendar cal = new GregorianCalendar(locale);
+ localeData.firstDayOfWeek = cal.getFirstDayOfWeek() - 1;
+
+ /*
+ * Date formatting (MM/DD/YYYY etc.)
+ */
+
+ DateFormat dateFormat = DateFormat.getDateTimeInstance(
+ DateFormat.SHORT, DateFormat.SHORT, locale);
+ if (!(dateFormat instanceof SimpleDateFormat)) {
+ getLogger().warning(
+ "Unable to get default date pattern for locale "
+ + locale.toString());
+ dateFormat = new SimpleDateFormat();
+ }
+ final String df = ((SimpleDateFormat) dateFormat).toPattern();
+
+ int timeStart = df.indexOf("H");
+ if (timeStart < 0) {
+ timeStart = df.indexOf("h");
+ }
+ final int ampm_first = df.indexOf("a");
+ // E.g. in Korean locale AM/PM is before h:mm
+ // TODO should take that into consideration on client-side as well,
+ // now always h:mm a
+ if (ampm_first > 0 && ampm_first < timeStart) {
+ timeStart = ampm_first;
+ }
+ // Hebrew locale has time before the date
+ final boolean timeFirst = timeStart == 0;
+ String dateformat;
+ if (timeFirst) {
+ int dateStart = df.indexOf(' ');
+ if (ampm_first > dateStart) {
+ dateStart = df.indexOf(' ', ampm_first);
+ }
+ dateformat = df.substring(dateStart + 1);
+ } else {
+ dateformat = df.substring(0, timeStart - 1);
+ }
+
+ localeData.dateFormat = dateformat.trim();
+
+ /*
+ * Time formatting (24 or 12 hour clock and AM/PM suffixes)
+ */
+ final String timeformat = df.substring(timeStart, df.length());
+ /*
+ * Doesn't return second or milliseconds.
+ *
+ * We use timeformat to determine 12/24-hour clock
+ */
+ final boolean twelve_hour_clock = timeformat.indexOf("a") > -1;
+ // TODO there are other possibilities as well, like 'h' in french
+ // (ignore them, too complicated)
+ final String hour_min_delimiter = timeformat.indexOf(".") > -1 ? "."
+ : ":";
+ // outWriter.print("\"tf\":\"" + timeformat + "\",");
+ localeData.twelveHourClock = twelve_hour_clock;
+ localeData.hourMinuteDelimiter = hour_min_delimiter;
+ if (twelve_hour_clock) {
+ final String[] ampm = dfs.getAmPmStrings();
+ localeData.am = ampm[0];
+ localeData.pm = ampm[1];
+ }
+
+ return localeData;
+ }
+
+ private static Logger getLogger() {
+ return Logger.getLogger(LocaleService.class.getName());
+ }
+
+}
diff --git a/server/src/com/vaadin/server/Page.java b/server/src/com/vaadin/server/Page.java
index d4c16fe7f7..11553527e0 100644
--- a/server/src/com/vaadin/server/Page.java
+++ b/server/src/com/vaadin/server/Page.java
@@ -21,11 +21,10 @@ import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.EventObject;
-import java.util.HashMap;
import java.util.Iterator;
+import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
-import java.util.Map;
import com.vaadin.event.EventRouter;
import com.vaadin.shared.ui.BorderStyle;
@@ -307,6 +306,61 @@ public class Page implements Serializable {
}
}
+ private static interface InjectedStyle {
+ public void paint(int id, PaintTarget target) throws PaintException;
+ }
+
+ private static class InjectedStyleString implements InjectedStyle {
+
+ private String css;
+
+ public InjectedStyleString(String css) {
+ this.css = css;
+ }
+
+ @Override
+ public void paint(int id, PaintTarget target) throws PaintException {
+ target.startTag("css-string");
+ target.addAttribute("id", id);
+ target.addText(css);
+ target.endTag("css-string");
+ }
+ }
+
+ private static class InjectedStyleResource implements InjectedStyle {
+
+ private final Resource resource;
+
+ public InjectedStyleResource(Resource resource) {
+ this.resource = resource;
+ }
+
+ @Override
+ public void paint(int id, PaintTarget target) throws PaintException {
+ target.startTag("css-resource");
+ target.addAttribute("id", id);
+ target.addAttribute("url", resource);
+ target.endTag("css-resource");
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this) {
+ return true;
+ } else if (obj instanceof InjectedStyleResource) {
+ InjectedStyleResource that = (InjectedStyleResource) obj;
+ return resource.equals(that.resource);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return resource.hashCode();
+ }
+ }
+
/**
* Contains dynamically injected styles injected in the HTML document at
* runtime.
@@ -315,16 +369,9 @@ public class Page implements Serializable {
*/
public static class Styles implements Serializable {
- private final Map<Integer, String> stringInjections = new HashMap<Integer, String>();
-
- private final Map<Integer, Resource> resourceInjections = new HashMap<Integer, Resource>();
+ private LinkedHashSet<InjectedStyle> injectedStyles = new LinkedHashSet<InjectedStyle>();
- // The combined injection counter between both string and resource
- // injections. Used as the key for the injection maps
- private int injectionCounter = 0;
-
- // Points to the next injection that has not yet been made into the Page
- private int nextInjectionPosition = 0;
+ private LinkedHashSet<InjectedStyle> pendingInjections = new LinkedHashSet<InjectedStyle>();
private final UI ui;
@@ -344,7 +391,7 @@ public class Page implements Serializable {
"Cannot inject null CSS string");
}
- stringInjections.put(injectionCounter++, css);
+ pendingInjections.add(new InjectedStyleString(css));
ui.markAsDirty();
}
@@ -360,43 +407,33 @@ public class Page implements Serializable {
"Cannot inject null resource");
}
- resourceInjections.put(injectionCounter++, resource);
- ui.markAsDirty();
+ InjectedStyleResource injection = new InjectedStyleResource(
+ resource);
+ if (!injectedStyles.contains(injection)
+ && pendingInjections.add(injection)) {
+ ui.markAsDirty();
+ }
}
private void paint(PaintTarget target) throws PaintException {
// If full repaint repaint all injections
if (target.isFullRepaint()) {
- nextInjectionPosition = 0;
+ injectedStyles.addAll(pendingInjections);
+ pendingInjections = injectedStyles;
+ injectedStyles = new LinkedHashSet<InjectedStyle>();
}
- if (injectionCounter > nextInjectionPosition) {
+ if (!pendingInjections.isEmpty()) {
target.startTag("css-injections");
- while (injectionCounter > nextInjectionPosition) {
-
- String stringInjection = stringInjections
- .get(nextInjectionPosition);
- if (stringInjection != null) {
- target.startTag("css-string");
- target.addAttribute("id", nextInjectionPosition);
- target.addText(stringInjection);
- target.endTag("css-string");
- }
-
- Resource resourceInjection = resourceInjections
- .get(nextInjectionPosition);
- if (resourceInjection != null) {
- target.startTag("css-resource");
- target.addAttribute("id", nextInjectionPosition);
- target.addAttribute("url", resourceInjection);
- target.endTag("css-resource");
- }
-
- nextInjectionPosition++;
+ for (InjectedStyle pending : pendingInjections) {
+ int id = injectedStyles.size();
+ pending.paint(id, target);
+ injectedStyles.add(pending);
}
+ pendingInjections.clear();
target.endTag("css-injections");
}
diff --git a/server/src/com/vaadin/server/RequestHandler.java b/server/src/com/vaadin/server/RequestHandler.java
index 873752c5f2..097a3e034b 100644
--- a/server/src/com/vaadin/server/RequestHandler.java
+++ b/server/src/com/vaadin/server/RequestHandler.java
@@ -37,7 +37,8 @@ public interface RequestHandler extends Serializable {
* using VaadinSession or anything inside the VaadinSession you must ensure
* the session is locked. This can be done by extending
* {@link SynchronizedRequestHandler} or by using
- * {@link VaadinSession#access(Runnable)} or {@link UI#access(Runnable)}.
+ * {@link VaadinSession#accessSynchronously(Runnable)} or
+ * {@link UI#accessSynchronously(Runnable)}.
* </p>
*
* @param session
diff --git a/server/src/com/vaadin/server/UIProvider.java b/server/src/com/vaadin/server/UIProvider.java
index 0305b907e6..3e7c85aea9 100644
--- a/server/src/com/vaadin/server/UIProvider.java
+++ b/server/src/com/vaadin/server/UIProvider.java
@@ -25,6 +25,7 @@ import com.vaadin.annotations.Theme;
import com.vaadin.annotations.Title;
import com.vaadin.annotations.Widgetset;
import com.vaadin.shared.communication.PushMode;
+import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.ui.UI;
public abstract class UIProvider implements Serializable {
@@ -174,4 +175,27 @@ public abstract class UIProvider implements Serializable {
return push.value();
}
}
+
+ /**
+ * Finds the {@link Transport} to use for a specific UI. If no transport is
+ * defined, <code>null</code> is returned.
+ * <p>
+ * The default implementation uses the @{@link Push} annotation if it's
+ * defined for the UI class.
+ *
+ * @param event
+ * the UI create event with information about the UI and the
+ * current request.
+ * @return the transport type to use, or <code>null</code> if the default
+ * transport type should be used
+ */
+ public Transport getPushTransport(UICreateEvent event) {
+ Push push = getAnnotationFor(event.getUIClass(), Push.class);
+ if (push == null) {
+ return null;
+ } else {
+ return push.transport();
+ }
+ }
+
}
diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java
index 327ce78a6c..d86e5e6507 100644
--- a/server/src/com/vaadin/server/VaadinPortlet.java
+++ b/server/src/com/vaadin/server/VaadinPortlet.java
@@ -287,7 +287,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
@Override
public void init(PortletConfig config) throws PortletException {
CurrentInstance.clearAll();
- setCurrent(this);
super.init(config);
Properties initParameters = new Properties();
@@ -407,7 +406,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
PortletResponse response) throws PortletException, IOException {
CurrentInstance.clearAll();
- setCurrent(this);
try {
getService().handleRequest(createVaadinRequest(request),
createVaadinResponse(response));
@@ -495,36 +493,23 @@ public class VaadinPortlet extends GenericPortlet implements Constants,
* portlet is defined (see {@link InheritableThreadLocal}). In other cases,
* (e.g. from background threads started in some other way), the current
* portlet is not automatically defined.
+ * <p>
+ * The current portlet is derived from the current service using
+ * {@link VaadinService#getCurrent()}
*
* @return the current vaadin portlet instance if available, otherwise
* <code>null</code>
*
- * @see #setCurrent(VaadinPortlet)
- *
* @since 7.0
*/
public static VaadinPortlet getCurrent() {
- return CurrentInstance.get(VaadinPortlet.class);
- }
-
- /**
- * Sets the current Vaadin portlet. This method is used by the framework to
- * set the current portlet whenever a new request is processed and it is
- * cleared when the request has been processed.
- * <p>
- * The application developer can also use this method to define the current
- * portlet outside the normal request handling, e.g. when initiating custom
- * background threads.
- * </p>
- *
- * @param portlet
- * the Vaadin portlet to register as the current portlet
- *
- * @see #getCurrent()
- * @see InheritableThreadLocal
- */
- public static void setCurrent(VaadinPortlet portlet) {
- CurrentInstance.setInheritable(VaadinPortlet.class, portlet);
+ VaadinService vaadinService = CurrentInstance.get(VaadinService.class);
+ if (vaadinService instanceof VaadinPortletService) {
+ VaadinPortletService vps = (VaadinPortletService) vaadinService;
+ return vps.getPortlet();
+ } else {
+ return null;
+ }
}
}
diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java
index af0c280c19..a040c72175 100644
--- a/server/src/com/vaadin/server/VaadinService.java
+++ b/server/src/com/vaadin/server/VaadinService.java
@@ -33,6 +33,9 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Level;
@@ -47,6 +50,7 @@ import org.json.JSONObject;
import com.vaadin.annotations.PreserveOnRefresh;
import com.vaadin.event.EventRouter;
+import com.vaadin.server.VaadinSession.FutureAccess;
import com.vaadin.server.communication.FileUploadHandler;
import com.vaadin.server.communication.HeartbeatHandler;
import com.vaadin.server.communication.PublishedFileHandler;
@@ -110,6 +114,11 @@ public abstract class VaadinService implements Serializable {
private boolean pushWarningEmitted = false;
/**
+ * Has {@link #init()} been run?
+ */
+ private boolean initialized = false;
+
+ /**
* Creates a new vaadin service based on a deployment configuration
*
* @param deploymentConfiguration
@@ -148,6 +157,8 @@ public abstract class VaadinService implements Serializable {
List<RequestHandler> handlers = createRequestHandlers();
Collections.reverse(handlers);
requestHandlers = Collections.unmodifiableCollection(handlers);
+
+ initialized = true;
}
/**
@@ -407,12 +418,12 @@ public abstract class VaadinService implements Serializable {
*/
public void fireSessionDestroy(VaadinSession vaadinSession) {
final VaadinSession session = vaadinSession;
- session.access(new Runnable() {
+ session.accessSynchronously(new Runnable() {
@Override
public void run() {
ArrayList<UI> uis = new ArrayList<UI>(session.getUIs());
for (final UI ui : uis) {
- ui.access(new Runnable() {
+ ui.accessSynchronously(new Runnable() {
@Override
public void run() {
/*
@@ -1087,7 +1098,7 @@ public abstract class VaadinService implements Serializable {
private void removeClosedUIs(final VaadinSession session) {
ArrayList<UI> uis = new ArrayList<UI>(session.getUIs());
for (final UI ui : uis) {
- ui.access(new Runnable() {
+ ui.accessSynchronously(new Runnable() {
@Override
public void run() {
if (ui.isClosing()) {
@@ -1224,6 +1235,10 @@ public abstract class VaadinService implements Serializable {
* The response
*/
public void requestStart(VaadinRequest request, VaadinResponse response) {
+ if (!initialized) {
+ throw new IllegalStateException(
+ "Can not process requests before init() has been called");
+ }
setCurrentInstances(request, response);
request.setAttribute(REQUEST_START_TIME_ATTRIBUTE, System.nanoTime());
}
@@ -1245,7 +1260,7 @@ public abstract class VaadinService implements Serializable {
if (session != null) {
final VaadinSession finalSession = session;
- session.access(new Runnable() {
+ session.accessSynchronously(new Runnable() {
@Override
public void run() {
cleanupSession(finalSession);
@@ -1254,7 +1269,7 @@ public abstract class VaadinService implements Serializable {
final long duration = (System.nanoTime() - (Long) request
.getAttribute(REQUEST_START_TIME_ATTRIBUTE)) / 1000000;
- session.access(new Runnable() {
+ session.accessSynchronously(new Runnable() {
@Override
public void run() {
finalSession.setLastRequestDuration(duration);
@@ -1542,8 +1557,9 @@ public abstract class VaadinService implements Serializable {
/**
* Checks that another {@link VaadinSession} instance is not locked. This is
- * internally used by {@link VaadinSession#access(Runnable)} and
- * {@link UI#access(Runnable)} to help avoid causing deadlocks.
+ * internally used by {@link VaadinSession#accessSynchronously(Runnable)}
+ * and {@link UI#accessSynchronously(Runnable)} to help avoid causing
+ * deadlocks.
*
* @since 7.1
* @param session
@@ -1597,4 +1613,88 @@ public abstract class VaadinService implements Serializable {
return true;
}
+ /**
+ * Implementation for {@link VaadinSession#access(Runnable)}. This method is
+ * implemented here instead of in {@link VaadinSession} to enable overriding
+ * the implementation without using a custom subclass of VaadinSession.
+ *
+ * @since 7.1
+ * @see VaadinSession#access(Runnable)
+ *
+ * @param session
+ * the vaadin session to access
+ * @param runnable
+ * the runnable to run with the session locked
+ *
+ * @return a future that can be used to check for task completion and to
+ * cancel the task
+ */
+ public Future<Void> accessSession(VaadinSession session, Runnable runnable) {
+ FutureAccess future = new FutureAccess(session, runnable);
+ session.getPendingAccessQueue().add(future);
+
+ /*
+ * If no thread is currently holding the lock, pending changes for UIs
+ * with automatic push would not be processed and pushed until the next
+ * time there is a request or someone does an explicit push call.
+ *
+ * To remedy this, we try to get the lock at this point. If the lock is
+ * currently held by another thread, we just back out as the queue will
+ * get purged once it is released. If the lock is held by the current
+ * thread, we just release it knowing that the queue gets purged once
+ * the lock is ultimately released. If the lock is not held by any
+ * thread and we acquire it, we just release it again to purge the queue
+ * right away.
+ */
+ try {
+ // tryLock() would be shorter, but it does not guarantee fairness
+ if (session.getLockInstance().tryLock(0, TimeUnit.SECONDS)) {
+ // unlock triggers runPendingAccessTasks
+ session.unlock();
+ }
+ } catch (InterruptedException e) {
+ // Just ignore
+ }
+
+ return future;
+ }
+
+ /**
+ * Purges the queue of pending access invocations enqueued with
+ * {@link VaadinSession#access(Runnable)}.
+ * <p>
+ * This method is automatically run by the framework at appropriate
+ * situations and is not intended to be used by application developers.
+ *
+ * @param session
+ * the vaadin session to purge the queue for
+ * @since 7.1
+ */
+ public void runPendingAccessTasks(VaadinSession session) {
+ assert session.hasLock();
+
+ if (session.getPendingAccessQueue().isEmpty()) {
+ return;
+ }
+
+ Map<Class<?>, CurrentInstance> oldInstances = CurrentInstance
+ .getInstances(false);
+
+ FutureAccess pendingAccess;
+ try {
+ while ((pendingAccess = session.getPendingAccessQueue().poll()) != null) {
+ if (!pendingAccess.isCancelled()) {
+ CurrentInstance.clearAll();
+ CurrentInstance.restoreInstances(pendingAccess
+ .getCurrentInstances());
+ CurrentInstance.setCurrent(session);
+ pendingAccess.run();
+ }
+ }
+ } finally {
+ CurrentInstance.clearAll();
+ CurrentInstance.restoreInstances(oldInstances);
+ }
+ }
+
}
diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java
index de074941c1..05e3335c00 100644
--- a/server/src/com/vaadin/server/VaadinServlet.java
+++ b/server/src/com/vaadin/server/VaadinServlet.java
@@ -21,6 +21,7 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
@@ -38,6 +39,8 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import com.vaadin.annotations.VaadinServletConfiguration;
+import com.vaadin.annotations.VaadinServletConfiguration.InitParameterName;
import com.vaadin.sass.internal.ScssStylesheet;
import com.vaadin.server.communication.ServletUIInitHandler;
import com.vaadin.shared.JsonConstants;
@@ -63,10 +66,11 @@ public class VaadinServlet extends HttpServlet implements Constants {
public void init(javax.servlet.ServletConfig servletConfig)
throws ServletException {
CurrentInstance.clearAll();
- setCurrent(this);
super.init(servletConfig);
Properties initParameters = new Properties();
+ readConfigurationAnnotation(initParameters);
+
// Read default parameters from server.xml
final ServletContext context = servletConfig.getServletContext();
for (final Enumeration<String> e = context.getInitParameterNames(); e
@@ -97,6 +101,39 @@ public class VaadinServlet extends HttpServlet implements Constants {
CurrentInstance.clearAll();
}
+ private void readConfigurationAnnotation(Properties initParameters)
+ throws ServletException {
+ VaadinServletConfiguration configAnnotation = UIProvider
+ .getAnnotationFor(getClass(), VaadinServletConfiguration.class);
+ if (configAnnotation != null) {
+ Method[] methods = VaadinServletConfiguration.class
+ .getDeclaredMethods();
+ for (Method method : methods) {
+ InitParameterName name = method
+ .getAnnotation(InitParameterName.class);
+ assert name != null : "All methods declared in VaadinServletConfiguration should have a @InitParameterName annotation";
+
+ try {
+ Object value = method.invoke(configAnnotation);
+
+ String stringValue;
+ if (value instanceof Class<?>) {
+ stringValue = ((Class<?>) value).getName();
+ } else {
+ stringValue = value.toString();
+ }
+
+ initParameters.setProperty(name.value(), stringValue);
+ } catch (Exception e) {
+ // This should never happen
+ throw new ServletException(
+ "Could not read @VaadinServletConfiguration value "
+ + method.getName(), e);
+ }
+ }
+ }
+ }
+
protected void servletInitialized() throws ServletException {
// Empty by default
}
@@ -108,36 +145,23 @@ public class VaadinServlet extends HttpServlet implements Constants {
* servlet is defined (see {@link InheritableThreadLocal}). In other cases,
* (e.g. from background threads started in some other way), the current
* servlet is not automatically defined.
+ * <p>
+ * The current servlet is derived from the current service using
+ * {@link VaadinService#getCurrent()}
*
* @return the current Vaadin servlet instance if available, otherwise
* <code>null</code>
*
- * @see #setCurrent(VaadinServlet)
- *
* @since 7.0
*/
public static VaadinServlet getCurrent() {
- return CurrentInstance.get(VaadinServlet.class);
- }
-
- /**
- * Sets the current Vaadin servlet. This method is used by the framework to
- * set the current servlet whenever a new request is processed and it is
- * cleared when the request has been processed.
- * <p>
- * The application developer can also use this method to define the current
- * servlet outside the normal request handling, e.g. when initiating custom
- * background threads.
- * </p>
- *
- * @param servlet
- * the Vaadin servlet to register as the current servlet
- *
- * @see #getCurrent()
- * @see InheritableThreadLocal
- */
- public static void setCurrent(VaadinServlet servlet) {
- CurrentInstance.setInheritable(VaadinServlet.class, servlet);
+ VaadinService vaadinService = CurrentInstance.get(VaadinService.class);
+ if (vaadinService instanceof VaadinServletService) {
+ VaadinServletService vss = (VaadinServletService) vaadinService;
+ return vss.getServlet();
+ } else {
+ return null;
+ }
}
protected DeploymentConfiguration createDeploymentConfiguration(
@@ -179,7 +203,6 @@ public class VaadinServlet extends HttpServlet implements Constants {
return;
}
CurrentInstance.clearAll();
- setCurrent(this);
VaadinServletRequest vaadinRequest = createVaadinRequest(request);
VaadinServletResponse vaadinResponse = createVaadinResponse(response);
@@ -188,8 +211,14 @@ public class VaadinServlet extends HttpServlet implements Constants {
}
if (isStaticResourceRequest(request)) {
- serveStaticResources(request, response);
- return;
+ // Define current servlet and service, but no request and response
+ getService().setCurrentInstances(null, null);
+ try {
+ serveStaticResources(request, response);
+ return;
+ } finally {
+ CurrentInstance.clearAll();
+ }
}
try {
getService().handleRequest(vaadinRequest, vaadinResponse);
diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java
index 317ea6cf7b..e0a5b51baa 100644
--- a/server/src/com/vaadin/server/VaadinSession.java
+++ b/server/src/com/vaadin/server/VaadinSession.java
@@ -25,7 +25,12 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.Queue;
import java.util.UUID;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.FutureTask;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.logging.Logger;
@@ -63,6 +68,68 @@ import com.vaadin.util.ReflectTools;
public class VaadinSession implements HttpSessionBindingListener, Serializable {
/**
+ * Encapsulates a {@link Runnable} submitted using
+ * {@link VaadinSession#access(Runnable)}. This class is used internally by
+ * the framework and is not intended to be directly used by application
+ * developers.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+ public static class FutureAccess extends FutureTask<Void> {
+ /**
+ * Snapshot of all non-inheritable current instances at the time this
+ * object was created.
+ */
+ private final Map<Class<?>, CurrentInstance> instances = CurrentInstance
+ .getInstances(true);
+ private final VaadinSession session;
+
+ /**
+ * Creates an instance for the given runnable
+ *
+ * @param session
+ * the session to which the task belongs
+ *
+ * @param runnable
+ * the runnable to run when this task is purged from the
+ * queue
+ */
+ public FutureAccess(VaadinSession session, Runnable runnable) {
+ super(runnable, null);
+ this.session = session;
+ }
+
+ @Override
+ public Void get() throws InterruptedException, ExecutionException {
+ /*
+ * Help the developer avoid programming patterns that cause
+ * deadlocks unless implemented very carefully. get(long, TimeUnit)
+ * does not have the same detection since a sensible timeout should
+ * avoid completely locking up the application.
+ *
+ * Even though no deadlock could occur after the runnable has been
+ * run, the check is always done as the deterministic behavior makes
+ * it easier to detect potential problems.
+ */
+ VaadinService.verifyNoOtherSessionLocked(session);
+ return super.get();
+ }
+
+ /**
+ * Gets the current instance values that should be used when running
+ * this task.
+ *
+ * @see CurrentInstance#restoreInstances(Map)
+ *
+ * @return a map of current instances.
+ */
+ public Map<Class<?>, CurrentInstance> getCurrentInstances() {
+ return instances;
+ }
+ }
+
+ /**
* The name of the parameter that is by default used in e.g. web.xml to
* define the name of the default {@link UI} class.
*/
@@ -130,6 +197,13 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
private transient Lock lock;
+ /*
+ * Pending tasks can't be serialized and the queue should be empty when the
+ * session is serialized as long as it doesn't happen while some other
+ * thread has the lock.
+ */
+ private transient final ConcurrentLinkedQueue<FutureAccess> pendingAccessQueue = new ConcurrentLinkedQueue<FutureAccess>();
+
/**
* Create a new service session tied to a Vaadin service
*
@@ -189,9 +263,9 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
/**
* Get the web browser associated with this session.
*
- * @return
- * @deprecated As of 7.0. Will likely change or be removed in a future
- * version
+ * @return the web browser object
+ *
+ * @deprecated As of 7.0, use {@link Page#getWebBrowser()} instead.
*/
@Deprecated
public WebBrowser getBrowser() {
@@ -820,11 +894,15 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
public void unlock() {
assert hasLock();
try {
+ /*
+ * Run pending tasks and push if the reentrant lock will actually be
+ * released by this unlock() invocation.
+ */
if (((ReentrantLock) getLockInstance()).getHoldCount() == 1) {
- // Only push if the reentrant lock will actually be released by
- // this unlock() invocation.
+ getService().runPendingAccessTasks(this);
+
for (UI ui : getUIs()) {
- if (ui.getPushMode() == PushMode.AUTOMATIC) {
+ if (ui.getPushConfiguration().getPushMode() == PushMode.AUTOMATIC) {
ui.push();
}
}
@@ -1063,23 +1141,26 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
}
/**
- * Provides exclusive access to this session from outside a request handling
- * thread.
+ * Locks this session and runs the provided Runnable right away.
* <p>
- * The given runnable is executed while holding the session lock to ensure
- * exclusive access to this session. The session and related thread locals
- * are set properly before executing the runnable.
- * </p>
- * <p>
- * RPC handlers for components inside this session do not need this method
- * as the session is automatically locked by the framework during request
- * handling.
+ * It is generally recommended to use {@link #access(Runnable)} instead of
+ * this method for accessing a session from a different thread as
+ * {@link #access(Runnable)} can be used while holding the lock of another
+ * session. To avoid causing deadlocks, this methods throws an exception if
+ * it is detected than another session is also locked by the current thread.
* </p>
* <p>
- * Note that calling this method while another session is locked by the
- * current thread will cause an exception. This is to prevent deadlock
- * situations when two threads have locked one session each and are both
- * waiting for the lock for the other session.
+ * This method behaves differently than {@link #access(Runnable)} in some
+ * situations:
+ * <ul>
+ * <li>If the current thread is currently holding the lock of this session,
+ * {@link #accessSynchronously(Runnable)} runs the task right away whereas
+ * {@link #access(Runnable)} defers the task to a later point in time.</li>
+ * <li>If some other thread is currently holding the lock for this session,
+ * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
+ * to be available whereas {@link #access(Runnable)} defers the task to a
+ * later point in time.</li>
+ * </ul>
* </p>
*
* @param runnable
@@ -1088,35 +1169,87 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable {
* @throws IllegalStateException
* if the current thread holds the lock for another session
*
+ * @since 7.1
*
* @see #lock()
* @see #getCurrent()
- * @see UI#access(Runnable)
+ * @see #access(Runnable)
+ * @see UI#accessSynchronously(Runnable)
*/
- public void access(Runnable runnable) {
+ public void accessSynchronously(Runnable runnable) {
VaadinService.verifyNoOtherSessionLocked(this);
Map<Class<?>, CurrentInstance> old = null;
lock();
try {
- old = CurrentInstance.setThreadLocals(this);
+ old = CurrentInstance.setCurrent(this);
runnable.run();
} finally {
unlock();
if (old != null) {
- CurrentInstance.restoreThreadLocals(old);
+ CurrentInstance.restoreInstances(old);
}
}
}
/**
- * @deprecated As of 7.1.0.beta1, use {@link #access(Runnable)} instead.
- * This method will be removed before the final 7.1.0 release.
+ * Provides exclusive access to this session from outside a request handling
+ * thread.
+ * <p>
+ * The given runnable is executed while holding the session lock to ensure
+ * exclusive access to this session. If this session is not locked, the lock
+ * will be acquired and the runnable is run right away. If this session is
+ * currently locked, the runnable will be run before that lock is released.
+ * </p>
+ * <p>
+ * RPC handlers for components inside this session do not need to use this
+ * method as the session is automatically locked by the framework during RPC
+ * handling.
+ * </p>
+ * <p>
+ * Please note that the runnable might be invoked on a different thread or
+ * later on the current thread, which means that custom thread locals might
+ * not have the expected values when the runnable is executed. Inheritable
+ * values in {@link CurrentInstance} will have the same values as when this
+ * method was invoked. {@link VaadinSession#getCurrent()} and
+ * {@link VaadinService#getCurrent()} are set according to this session
+ * before executing the runnable. Non-inheritable CurrentInstance values
+ * including {@link VaadinService#getCurrentRequest()} and
+ * {@link VaadinService#getCurrentResponse()} will not be defined.
+ * </p>
+ * <p>
+ * The returned future can be used to check for task completion and to
+ * cancel the task. To help avoiding deadlocks, {@link Future#get()} throws
+ * an exception if it is detected that the current thread holds the lock for
+ * some other session.
+ * </p>
+ *
+ * @see #lock()
+ * @see #getCurrent()
+ * @see #accessSynchronously(Runnable)
+ * @see UI#access(Runnable)
+ *
+ * @since 7.1
+ *
+ * @param runnable
+ * the runnable which accesses the session
+ * @return a future that can be used to check for task completion and to
+ * cancel the task
*/
- @Deprecated
- public void runSafely(Runnable runnable) {
- access(runnable);
+ public Future<Void> access(Runnable runnable) {
+ return getService().accessSession(this, runnable);
+ }
+
+ /**
+ * Gets the queue of tasks submitted using {@link #access(Runnable)}.
+ *
+ * @since 7.1
+ *
+ * @return the pending access queue
+ */
+ public Queue<FutureAccess> getPendingAccessQueue() {
+ return pendingAccessQueue;
}
/**
diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
index 0bba65ff1d..9e57ccb45d 100644
--- a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
+++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
@@ -32,7 +32,7 @@ import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResource.TRANSPORT;
import org.json.JSONException;
-import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.communication.PushConstants;
import com.vaadin.ui.UI;
/**
@@ -42,21 +42,22 @@ import com.vaadin.ui.UI;
* @author Vaadin Ltd
* @since 7.1
*/
-public class AtmospherePushConnection implements Serializable, PushConnection {
+public class AtmospherePushConnection implements PushConnection {
/**
* Represents a message that can arrive as multiple fragments.
*/
- protected static class FragmentedMessage {
+ protected static class FragmentedMessage implements Serializable {
private final StringBuilder message = new StringBuilder();
private final int messageLength;
public FragmentedMessage(Reader reader) throws IOException {
- // Messages are prefixed by the total message length plus '|'
+ // Messages are prefixed by the total message length plus a
+ // delimiter
String length = "";
int c;
while ((c = reader.read()) != -1
- && c != ApplicationConstants.WEBSOCKET_MESSAGE_DELIMITER) {
+ && c != PushConstants.MESSAGE_DELIMITER) {
length += (char) c;
}
try {
@@ -76,7 +77,7 @@ public class AtmospherePushConnection implements Serializable, PushConnection {
* @throws IOException
*/
public boolean append(Reader reader) throws IOException {
- char[] buffer = new char[ApplicationConstants.WEBSOCKET_BUFFER_SIZE];
+ char[] buffer = new char[PushConstants.WEBSOCKET_BUFFER_SIZE];
int read;
while ((read = reader.read(buffer)) != -1) {
message.append(buffer, 0, read);
@@ -122,7 +123,7 @@ public class AtmospherePushConnection implements Serializable, PushConnection {
protected void push(boolean async) throws IOException {
Writer writer = new StringWriter();
try {
- new UidlWriter().write(getUI(), writer, false, false, async);
+ new UidlWriter().write(getUI(), writer, false, async);
} catch (JSONException e) {
throw new IOException("Error writing UIDL", e);
}
diff --git a/server/src/com/vaadin/server/communication/FileUploadHandler.java b/server/src/com/vaadin/server/communication/FileUploadHandler.java
index e875a4e861..e9569d45a1 100644
--- a/server/src/com/vaadin/server/communication/FileUploadHandler.java
+++ b/server/src/com/vaadin/server/communication/FileUploadHandler.java
@@ -632,7 +632,7 @@ public class FileUploadHandler implements RequestHandler {
private void cleanStreamVariable(VaadinSession session,
final ClientConnector owner, final String variableName) {
- session.access(new Runnable() {
+ session.accessSynchronously(new Runnable() {
@Override
public void run() {
owner.getUI()
diff --git a/server/src/com/vaadin/server/communication/LocaleWriter.java b/server/src/com/vaadin/server/communication/LocaleWriter.java
deleted file mode 100644
index c05649da19..0000000000
--- a/server/src/com/vaadin/server/communication/LocaleWriter.java
+++ /dev/null
@@ -1,204 +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.server.communication;
-
-import java.io.IOException;
-import java.io.Serializable;
-import java.io.Writer;
-import java.text.DateFormat;
-import java.text.DateFormatSymbols;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-import java.util.List;
-import java.util.Locale;
-import java.util.logging.Logger;
-
-/**
- * Serializes locale information to JSON.
- *
- * @author Vaadin Ltd
- * @since 7.1
- * @deprecated See <a href="http://dev.vaadin.com/ticket/11378">ticket
- * #11378</a>.
- */
-@Deprecated
-public class LocaleWriter implements Serializable {
-
- /**
- * Writes a JSON object containing localized strings of the given locales.
- *
- * @param locales
- * The list of {@link Locale}s to write.
- * @param writer
- * The {@link Writer} used to write the JSON.
- * @throws IOException
- * If the serialization fails.
- *
- */
- public void write(List<String> locales, Writer writer) throws IOException {
-
- // Send locale informations to client
- writer.write("[");
- // TODO locales are currently sent on each request; this will be fixed
- // by implementing #11378.
- for (int pendingLocalesIndex = 0; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) {
-
- final Locale l = generateLocale(locales.get(pendingLocalesIndex));
- // Locale name
- writer.write("{\"name\":\"" + l.toString() + "\",");
-
- /*
- * Month names (both short and full)
- */
- final DateFormatSymbols dfs = new DateFormatSymbols(l);
- final String[] short_months = dfs.getShortMonths();
- final String[] months = dfs.getMonths();
- writer.write("\"smn\":[\""
- + // ShortMonthNames
- short_months[0] + "\",\"" + short_months[1] + "\",\""
- + short_months[2] + "\",\"" + short_months[3] + "\",\""
- + short_months[4] + "\",\"" + short_months[5] + "\",\""
- + short_months[6] + "\",\"" + short_months[7] + "\",\""
- + short_months[8] + "\",\"" + short_months[9] + "\",\""
- + short_months[10] + "\",\"" + short_months[11] + "\""
- + "],");
- writer.write("\"mn\":[\""
- + // MonthNames
- months[0] + "\",\"" + months[1] + "\",\"" + months[2]
- + "\",\"" + months[3] + "\",\"" + months[4] + "\",\""
- + months[5] + "\",\"" + months[6] + "\",\"" + months[7]
- + "\",\"" + months[8] + "\",\"" + months[9] + "\",\""
- + months[10] + "\",\"" + months[11] + "\"" + "],");
-
- /*
- * Weekday names (both short and full)
- */
- final String[] short_days = dfs.getShortWeekdays();
- final String[] days = dfs.getWeekdays();
- writer.write("\"sdn\":[\""
- + // ShortDayNames
- short_days[1] + "\",\"" + short_days[2] + "\",\""
- + short_days[3] + "\",\"" + short_days[4] + "\",\""
- + short_days[5] + "\",\"" + short_days[6] + "\",\""
- + short_days[7] + "\"" + "],");
- writer.write("\"dn\":[\""
- + // DayNames
- days[1] + "\",\"" + days[2] + "\",\"" + days[3] + "\",\""
- + days[4] + "\",\"" + days[5] + "\",\"" + days[6] + "\",\""
- + days[7] + "\"" + "],");
-
- /*
- * First day of week (0 = sunday, 1 = monday)
- */
- final Calendar cal = new GregorianCalendar(l);
- writer.write("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ",");
-
- /*
- * Date formatting (MM/DD/YYYY etc.)
- */
-
- DateFormat dateFormat = DateFormat.getDateTimeInstance(
- DateFormat.SHORT, DateFormat.SHORT, l);
- if (!(dateFormat instanceof SimpleDateFormat)) {
- getLogger().warning(
- "Unable to get default date pattern for locale "
- + l.toString());
- dateFormat = new SimpleDateFormat();
- }
- final String df = ((SimpleDateFormat) dateFormat).toPattern();
-
- int timeStart = df.indexOf("H");
- if (timeStart < 0) {
- timeStart = df.indexOf("h");
- }
- final int ampm_first = df.indexOf("a");
- // E.g. in Korean locale AM/PM is before h:mm
- // TODO should take that into consideration on client-side as well,
- // now always h:mm a
- if (ampm_first > 0 && ampm_first < timeStart) {
- timeStart = ampm_first;
- }
- // Hebrew locale has time before the date
- final boolean timeFirst = timeStart == 0;
- String dateformat;
- if (timeFirst) {
- int dateStart = df.indexOf(' ');
- if (ampm_first > dateStart) {
- dateStart = df.indexOf(' ', ampm_first);
- }
- dateformat = df.substring(dateStart + 1);
- } else {
- dateformat = df.substring(0, timeStart - 1);
- }
-
- writer.write("\"df\":\"" + dateformat.trim() + "\",");
-
- /*
- * Time formatting (24 or 12 hour clock and AM/PM suffixes)
- */
- final String timeformat = df.substring(timeStart, df.length());
- /*
- * Doesn't return second or milliseconds.
- *
- * We use timeformat to determine 12/24-hour clock
- */
- final boolean twelve_hour_clock = timeformat.indexOf("a") > -1;
- // TODO there are other possibilities as well, like 'h' in french
- // (ignore them, too complicated)
- final String hour_min_delimiter = timeformat.indexOf(".") > -1 ? "."
- : ":";
- // outWriter.print("\"tf\":\"" + timeformat + "\",");
- writer.write("\"thc\":" + twelve_hour_clock + ",");
- writer.write("\"hmd\":\"" + hour_min_delimiter + "\"");
- if (twelve_hour_clock) {
- final String[] ampm = dfs.getAmPmStrings();
- writer.write(",\"ampm\":[\"" + ampm[0] + "\",\"" + ampm[1]
- + "\"]");
- }
- writer.write("}");
- if (pendingLocalesIndex < locales.size() - 1) {
- writer.write(",");
- }
- }
- writer.write("]"); // Close locales
- }
-
- /**
- * Constructs a {@link Locale} instance to be sent to the client based on a
- * short locale description string.
- *
- * @see #requireLocale(String)
- *
- * @param value
- * @return
- */
- private Locale generateLocale(String value) {
- final String[] temp = value.split("_");
- if (temp.length == 1) {
- return new Locale(temp[0]);
- } else if (temp.length == 2) {
- return new Locale(temp[0], temp[1]);
- } else {
- return new Locale(temp[0], temp[1], temp[2]);
- }
- }
-
- private static final Logger getLogger() {
- return Logger.getLogger(LocaleWriter.class.getName());
- }
-}
diff --git a/server/src/com/vaadin/server/communication/MetadataWriter.java b/server/src/com/vaadin/server/communication/MetadataWriter.java
index 1a3f0e946a..5ad7186c24 100644
--- a/server/src/com/vaadin/server/communication/MetadataWriter.java
+++ b/server/src/com/vaadin/server/communication/MetadataWriter.java
@@ -17,17 +17,11 @@
package com.vaadin.server.communication;
import java.io.IOException;
-import java.io.PrintWriter;
import java.io.Serializable;
import java.io.Writer;
-import java.util.List;
-import com.vaadin.server.ClientConnector;
-import com.vaadin.server.ComponentSizeValidator;
-import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
import com.vaadin.server.SystemMessages;
import com.vaadin.ui.UI;
-import com.vaadin.ui.Window;
/**
* Serializes miscellaneous metadata to JSON.
@@ -54,9 +48,6 @@ public class MetadataWriter implements Serializable {
* @param async
* True if this message is sent by the server asynchronously,
* false if it is a response to a client message.
- * @param hilightedConnector
- * The connector that should be highlighted on the client or null
- * if none.
* @param messages
* a {@link SystemMessages} containing client-side error
* messages.
@@ -64,27 +55,8 @@ public class MetadataWriter implements Serializable {
* If the serialization fails.
*
*/
- public void write(UI ui, Writer writer, boolean repaintAll,
- boolean analyzeLayouts, boolean async,
- ClientConnector hilightedConnector, SystemMessages messages)
- throws IOException {
-
- List<InvalidLayout> invalidComponentRelativeSizes = null;
-
- if (analyzeLayouts) {
- invalidComponentRelativeSizes = ComponentSizeValidator
- .validateComponentRelativeSizes(ui.getContent(), null, null);
-
- // Also check any existing subwindows
- if (ui.getWindows() != null) {
- for (Window subWindow : ui.getWindows()) {
- invalidComponentRelativeSizes = ComponentSizeValidator
- .validateComponentRelativeSizes(
- subWindow.getContent(),
- invalidComponentRelativeSizes, null);
- }
- }
- }
+ public void write(UI ui, Writer writer, boolean repaintAll, boolean async,
+ SystemMessages messages) throws IOException {
writer.write("{");
@@ -92,28 +64,6 @@ public class MetadataWriter implements Serializable {
if (repaintAll) {
metaOpen = true;
writer.write("\"repaintAll\":true");
- if (analyzeLayouts) {
- writer.write(", \"invalidLayouts\":");
- writer.write("[");
- if (invalidComponentRelativeSizes != null) {
- boolean first = true;
- for (InvalidLayout invalidLayout : invalidComponentRelativeSizes) {
- if (!first) {
- writer.write(",");
- } else {
- first = false;
- }
- invalidLayout.reportErrors(new PrintWriter(writer),
- System.err);
- }
- }
- writer.write("]");
- }
- if (hilightedConnector != null) {
- writer.write(", \"hl\":\"");
- writer.write(hilightedConnector.getConnectorId());
- writer.write("\"");
- }
}
if (async) {
diff --git a/server/src/com/vaadin/server/communication/PushConnection.java b/server/src/com/vaadin/server/communication/PushConnection.java
index 4e043f565f..bb88998402 100644
--- a/server/src/com/vaadin/server/communication/PushConnection.java
+++ b/server/src/com/vaadin/server/communication/PushConnection.java
@@ -16,6 +16,8 @@
package com.vaadin.server.communication;
+import java.io.Serializable;
+
import com.vaadin.ui.UI;
/**
@@ -25,7 +27,7 @@ import com.vaadin.ui.UI;
* @author Vaadin Ltd
* @since 7.1
*/
-public interface PushConnection {
+public interface PushConnection extends Serializable {
/**
* Pushes pending state changes and client RPC calls to the client. It is
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java
index e740db410d..7efcb8fd8c 100644
--- a/server/src/com/vaadin/server/communication/PushHandler.java
+++ b/server/src/com/vaadin/server/communication/PushHandler.java
@@ -84,6 +84,13 @@ public class PushHandler implements AtmosphereHandler {
if (browser.isIE() && browser.getBrowserMajorVersion() == 8) {
resource.padding(LONG_PADDING);
}
+
+ // Must ensure that the streaming response contains
+ // "Connection: close", otherwise iOS 6 will wait for the
+ // response to this request before sending another request to
+ // the same server (as it will apparently try to reuse the same
+ // connection)
+ resource.getResponse().addHeader("Connection", "close");
}
String requestToken = resource.getRequest().getParameter(
@@ -164,7 +171,7 @@ public class PushHandler implements AtmosphereHandler {
PushEventCallback disconnectCallback = new PushEventCallback() {
@Override
public void run(AtmosphereResource resource, UI ui) throws IOException {
- PushMode pushMode = ui.getPushMode();
+ PushMode pushMode = ui.getPushConfiguration().getPushMode();
AtmospherePushConnection pushConnection = getConnectionForUI(ui);
String id = resource.uuid();
@@ -331,9 +338,9 @@ public class PushHandler implements AtmosphereHandler {
writer.write(event.getMessage().toString());
switch (resource.transport()) {
- case SSE:
case WEBSOCKET:
break;
+ case SSE:
case STREAMING:
writer.flush();
break;
diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java
index 8360e08af9..8d0da24896 100644
--- a/server/src/com/vaadin/server/communication/PushRequestHandler.java
+++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java
@@ -23,6 +23,7 @@ import javax.servlet.ServletException;
import org.atmosphere.client.TrackMessageSizeInterceptor;
import org.atmosphere.cpr.ApplicationConfig;
import org.atmosphere.cpr.AtmosphereFramework;
+import org.atmosphere.cpr.AtmosphereInterceptor;
import org.atmosphere.cpr.AtmosphereRequest;
import org.atmosphere.cpr.AtmosphereResponse;
@@ -36,7 +37,7 @@ import com.vaadin.server.VaadinServletRequest;
import com.vaadin.server.VaadinServletResponse;
import com.vaadin.server.VaadinServletService;
import com.vaadin.server.VaadinSession;
-import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.communication.PushConstants;
/**
* Handles requests to open a push (bidirectional) communication channel between
@@ -55,15 +56,22 @@ public class PushRequestHandler implements RequestHandler,
public PushRequestHandler(VaadinServletService service)
throws ServiceException {
- atmosphere = new AtmosphereFramework();
+ atmosphere = new AtmosphereFramework() {
+ @Override
+ protected void analytics() {
+ // Overridden to disable version number check
+ }
+ };
pushHandler = new PushHandler(service);
atmosphere.addAtmosphereHandler("/*", pushHandler);
atmosphere.addInitParameter(ApplicationConfig.PROPERTY_SESSION_SUPPORT,
"true");
+ atmosphere.addInitParameter(ApplicationConfig.MESSAGE_DELIMITER,
+ String.valueOf(PushConstants.MESSAGE_DELIMITER));
final String bufferSize = String
- .valueOf(ApplicationConstants.WEBSOCKET_BUFFER_SIZE);
+ .valueOf(PushConstants.WEBSOCKET_BUFFER_SIZE);
atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_BUFFER_SIZE,
bufferSize);
atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_MAXTEXTSIZE,
@@ -75,12 +83,14 @@ public class PushRequestHandler implements RequestHandler,
atmosphere.addInitParameter("org.atmosphere.cpr.showSupportMessage",
"false");
- // Required to ensure the client-side knows at which points to split the
- // message stream into individual messages when using certain transports
- atmosphere.interceptor(new TrackMessageSizeInterceptor());
-
try {
atmosphere.init(service.getServlet().getServletConfig());
+
+ // Ensure the client-side knows how to split the message stream
+ // into individual messages when using certain transports
+ AtmosphereInterceptor trackMessageSize = new TrackMessageSizeInterceptor();
+ trackMessageSize.configure(atmosphere.getAtmosphereConfig());
+ atmosphere.interceptor(trackMessageSize);
} catch (ServletException e) {
throw new ServiceException("Could not read atmosphere settings", e);
}
diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java
index e4b5360b49..d4b0bc709f 100644
--- a/server/src/com/vaadin/server/communication/UIInitHandler.java
+++ b/server/src/com/vaadin/server/communication/UIInitHandler.java
@@ -39,6 +39,7 @@ import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.communication.PushMode;
+import com.vaadin.shared.ui.ui.Transport;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.ui.UI;
@@ -209,7 +210,12 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
pushMode = session.getService().getDeploymentConfiguration()
.getPushMode();
}
- ui.setPushMode(pushMode);
+ ui.getPushConfiguration().setPushMode(pushMode);
+
+ Transport transport = provider.getPushTransport(event);
+ if (transport != null) {
+ ui.getPushConfiguration().setTransport(transport);
+ }
// Set thread local here so it is available in init
UI.setCurrent(ui);
@@ -273,7 +279,7 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
if (session.getConfiguration().isXsrfProtectionEnabled()) {
writer.write(getSecurityKeyUIDL(session));
}
- new UidlWriter().write(uI, writer, true, false, false);
+ new UidlWriter().write(uI, writer, true, false);
writer.write("}");
String initialUIDL = writer.toString();
diff --git a/server/src/com/vaadin/server/communication/UidlRequestHandler.java b/server/src/com/vaadin/server/communication/UidlRequestHandler.java
index 73ff92f8bd..3564aa65b5 100644
--- a/server/src/com/vaadin/server/communication/UidlRequestHandler.java
+++ b/server/src/com/vaadin/server/communication/UidlRequestHandler.java
@@ -19,13 +19,11 @@ package com.vaadin.server.communication;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
-import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.json.JSONException;
-import com.vaadin.server.ClientConnector;
import com.vaadin.server.Constants;
import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException;
import com.vaadin.server.ServletPortletHelper;
@@ -39,7 +37,6 @@ import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.JsonConstants;
import com.vaadin.shared.Version;
-import com.vaadin.ui.Component;
import com.vaadin.ui.UI;
/**
@@ -79,32 +76,16 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements
checkWidgetsetVersion(request);
String requestThemeName = request.getParameter("theme");
- ClientConnector highlightedConnector;
// repaint requested or session has timed out and new one is created
boolean repaintAll;
- // TODO PUSH repaintAll, analyzeLayouts, highlightConnector should be
+ // TODO PUSH repaintAll, analyzeLayouts should be
// part of the message payload to make the functionality transport
// agnostic
repaintAll = (request
.getParameter(ApplicationConstants.URL_PARAMETER_REPAINT_ALL) != null);
- boolean analyzeLayouts = false;
- if (repaintAll) {
- // analyzing can be done only with repaintAll
- analyzeLayouts = (request
- .getParameter(ApplicationConstants.PARAM_ANALYZE_LAYOUTS) != null);
-
- String pid = request
- .getParameter(ApplicationConstants.PARAM_HIGHLIGHT_CONNECTOR);
- if (pid != null) {
- highlightedConnector = uI.getConnectorTracker().getConnector(
- pid);
- highlightConnector(highlightedConnector);
- }
- }
-
StringWriter stringWriter = new StringWriter();
try {
@@ -114,8 +95,7 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements
session.getCommunicationManager().repaintAll(uI);
}
- writeUidl(request, response, uI, stringWriter, repaintAll,
- analyzeLayouts);
+ writeUidl(request, response, uI, stringWriter, repaintAll);
} catch (JSONException e) {
getLogger().log(Level.SEVERE, "Error writing JSON to response", e);
// Refresh on client side
@@ -164,11 +144,11 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements
}
private void writeUidl(VaadinRequest request, VaadinResponse response,
- UI ui, Writer writer, boolean repaintAll, boolean analyzeLayouts)
- throws IOException, JSONException {
+ UI ui, Writer writer, boolean repaintAll) throws IOException,
+ JSONException {
openJsonMessage(writer, response);
- new UidlWriter().write(ui, writer, repaintAll, analyzeLayouts, false);
+ new UidlWriter().write(ui, writer, repaintAll, false);
closeJsonMessage(writer);
}
@@ -190,63 +170,6 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements
outWriter.write("for(;;);[{");
}
- // TODO Does this belong here?
- protected void highlightConnector(ClientConnector highlightedConnector) {
- StringBuilder sb = new StringBuilder();
- sb.append("*** Debug details of a connector: *** \n");
- sb.append("Type: ");
- sb.append(highlightedConnector.getClass().getName());
- sb.append("\nId:");
- sb.append(highlightedConnector.getConnectorId());
- if (highlightedConnector instanceof Component) {
- Component component = (Component) highlightedConnector;
- if (component.getCaption() != null) {
- sb.append("\nCaption:");
- sb.append(component.getCaption());
- }
- }
- printHighlightedConnectorHierarchy(sb, highlightedConnector);
- getLogger().info(sb.toString());
- }
-
- // TODO Does this belong here?
- protected void printHighlightedConnectorHierarchy(StringBuilder sb,
- ClientConnector connector) {
- LinkedList<ClientConnector> h = new LinkedList<ClientConnector>();
- h.add(connector);
- ClientConnector parent = connector.getParent();
- while (parent != null) {
- h.addFirst(parent);
- parent = parent.getParent();
- }
-
- sb.append("\nConnector hierarchy:\n");
- VaadinSession session2 = connector.getUI().getSession();
- sb.append(session2.getClass().getName());
- sb.append("(");
- sb.append(session2.getClass().getSimpleName());
- sb.append(".java");
- sb.append(":1)");
- int l = 1;
- for (ClientConnector connector2 : h) {
- sb.append("\n");
- for (int i = 0; i < l; i++) {
- sb.append(" ");
- }
- l++;
- Class<? extends ClientConnector> connectorClass = connector2
- .getClass();
- Class<?> topClass = connectorClass;
- while (topClass.getEnclosingClass() != null) {
- topClass = topClass.getEnclosingClass();
- }
- sb.append(connectorClass.getName());
- sb.append("(");
- sb.append(topClass.getSimpleName());
- sb.append(".java:1)");
- }
- }
-
private static final Logger getLogger() {
return Logger.getLogger(UidlRequestHandler.class.getName());
}
diff --git a/server/src/com/vaadin/server/communication/UidlWriter.java b/server/src/com/vaadin/server/communication/UidlWriter.java
index fbe2fb86d5..60933a75c2 100644
--- a/server/src/com/vaadin/server/communication/UidlWriter.java
+++ b/server/src/com/vaadin/server/communication/UidlWriter.java
@@ -71,12 +71,16 @@ public class UidlWriter implements Serializable {
* @throws JSONException
* If the JSON serialization fails.
*/
- public void write(UI ui, Writer writer, boolean repaintAll,
- boolean analyzeLayouts, boolean async) throws IOException,
- JSONException {
+ public void write(UI ui, Writer writer, boolean repaintAll, boolean async)
+ throws IOException, JSONException {
+ VaadinSession session = ui.getSession();
+
+ // Purge pending access calls as they might produce additional changes
+ // to write out
+ session.getService().runPendingAccessTasks(session);
+
ArrayList<ClientConnector> dirtyVisibleConnectors = ui
.getConnectorTracker().getDirtyVisibleConnectors();
- VaadinSession session = ui.getSession();
LegacyCommunicationManager manager = session.getCommunicationManager();
// Paints components
ConnectorTracker uiConnectorTracker = ui.getConnectorTracker();
@@ -156,8 +160,7 @@ public class UidlWriter implements Serializable {
SystemMessages messages = ui.getSession().getService()
.getSystemMessages(ui.getLocale(), null);
// TODO hilightedConnector
- new MetadataWriter().write(ui, writer, repaintAll, analyzeLayouts,
- async, null, messages);
+ new MetadataWriter().write(ui, writer, repaintAll, async, messages);
writer.write(", ");
writer.write("\"resources\" : ");
@@ -278,10 +281,6 @@ public class UidlWriter implements Serializable {
+ new JSONArray(styleDependencies).toString());
}
- // add any pending locale definitions requested by the client
- writer.write(", \"locales\": ");
- manager.printLocaleDeclarations(writer);
-
if (manager.getDragAndDropService() != null) {
manager.getDragAndDropService().printJSONResponse(writer);
}
diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java
index 06060dbf91..9ff36a42d2 100644
--- a/server/src/com/vaadin/ui/AbstractComponent.java
+++ b/server/src/com/vaadin/ui/AbstractComponent.java
@@ -291,7 +291,10 @@ public abstract class AbstractComponent extends AbstractClientConnector
public void setLocale(Locale locale) {
this.locale = locale;
- // FIXME: Reload value if there is a converter
+ if (locale != null && isAttached()) {
+ getUI().getLocaleService().addLocale(locale);
+ }
+
markAsDirty();
}
@@ -556,6 +559,10 @@ public abstract class AbstractComponent extends AbstractClientConnector
focus();
}
setActionManagerViewer();
+ if (locale != null) {
+ getUI().getLocaleService().addLocale(locale);
+ }
+
}
/*
diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java
index 3bca63a3b7..606bf5fb21 100644
--- a/server/src/com/vaadin/ui/AbstractField.java
+++ b/server/src/com/vaadin/ui/AbstractField.java
@@ -616,17 +616,14 @@ public abstract class AbstractField<T> extends AbstractComponent implements
// Check if the current converter is compatible.
if (newDataSource != null
- && !ConverterUtil.canConverterHandle(getConverter(), getType(),
- newDataSource.getType())) {
- // Changing from e.g. Number -> Double should set a new converter,
- // changing from Double -> Number can keep the old one (Property
- // accepts Number)
-
- // Set a new converter if there is a new data source and
- // there is no old converter or the old is incompatible.
+ && !ConverterUtil.canConverterPossiblyHandle(getConverter(),
+ getType(), newDataSource.getType())) {
+ // There is no converter set or there is no way the current
+ // converter can be compatible.
setConverter(newDataSource.getType());
}
- // Gets the value from source
+ // Gets the value from source. This requires that a valid converter has
+ // been set.
try {
if (dataSource != null) {
T fieldValue = convertFromModel(getDataSourceValue());
diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java
index 38fa355dd8..c3385baa2c 100644
--- a/server/src/com/vaadin/ui/Calendar.java
+++ b/server/src/com/vaadin/ui/Calendar.java
@@ -45,6 +45,8 @@ import com.vaadin.event.dd.DropHandler;
import com.vaadin.event.dd.DropTarget;
import com.vaadin.event.dd.TargetDetails;
import com.vaadin.server.KeyMapper;
+import com.vaadin.server.PaintException;
+import com.vaadin.server.PaintTarget;
import com.vaadin.shared.ui.calendar.CalendarEventId;
import com.vaadin.shared.ui.calendar.CalendarServerRpc;
import com.vaadin.shared.ui.calendar.CalendarState;
@@ -114,7 +116,7 @@ public class Calendar extends AbstractComponent implements
CalendarComponentEvents.RangeSelectNotifier,
CalendarComponentEvents.EventResizeNotifier,
CalendarEventProvider.EventSetChangeListener, DropTarget,
- CalendarEditableEventProvider, Action.Container {
+ CalendarEditableEventProvider, Action.Container, LegacyComponent {
/**
* Calendar can use either 12 hours clock or 24 hours clock.
@@ -1842,4 +1844,31 @@ public class Calendar extends AbstractComponent implements
}
}
}
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.server.VariableOwner#changeVariables(java.lang.Object,
+ * java.util.Map)
+ */
+ @Override
+ public void changeVariables(Object source, Map<String, Object> variables) {
+ /*
+ * Only defined to fulfill the LegacyComponent interface used for
+ * calendar drag & drop. No implementation required.
+ */
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.LegacyComponent#paintContent(com.vaadin.server.PaintTarget)
+ */
+ @Override
+ public void paintContent(PaintTarget target) throws PaintException {
+ if (dropHandler != null) {
+ dropHandler.getAcceptCriterion().paint(target);
+ }
+ }
} \ No newline at end of file
diff --git a/server/src/com/vaadin/ui/Label.java b/server/src/com/vaadin/ui/Label.java
index d037652a09..d7cee2a80d 100644
--- a/server/src/com/vaadin/ui/Label.java
+++ b/server/src/com/vaadin/ui/Label.java
@@ -242,14 +242,17 @@ public class Label extends AbstractComponent implements Property<String>,
((Property.ValueChangeNotifier) dataSource).removeListener(this);
}
+ // Check if the current converter is compatible.
if (newDataSource != null
- && !ConverterUtil.canConverterHandle(getConverter(),
- String.class, newDataSource.getType())) {
- // Try to find a converter
+ && !ConverterUtil.canConverterPossiblyHandle(getConverter(),
+ getType(), newDataSource.getType())) {
+ // There is no converter set or there is no way the current
+ // converter can be compatible.
Converter<String, ?> c = ConverterUtil.getConverter(String.class,
newDataSource.getType(), getSession());
setConverter(c);
}
+
dataSource = newDataSource;
if (dataSource != null) {
// Update the value from the data source. If data source was set to
diff --git a/server/src/com/vaadin/ui/LegacyWindow.java b/server/src/com/vaadin/ui/LegacyWindow.java
index 1b66b608c1..458b09390d 100644
--- a/server/src/com/vaadin/ui/LegacyWindow.java
+++ b/server/src/com/vaadin/ui/LegacyWindow.java
@@ -125,7 +125,7 @@ public class LegacyWindow extends UI {
public void setName(String name) {
this.name = name;
// The name can not be changed in application
- if (getSession() != null) {
+ if (isAttached()) {
throw new IllegalStateException(
"Window name can not be changed while "
+ "the window is in application");
diff --git a/server/src/com/vaadin/ui/LoginForm.java b/server/src/com/vaadin/ui/LoginForm.java
index d06882927e..67d7182ecb 100644
--- a/server/src/com/vaadin/ui/LoginForm.java
+++ b/server/src/com/vaadin/ui/LoginForm.java
@@ -68,7 +68,7 @@ public class LoginForm extends CustomComponent {
}
final StringBuilder responseBuilder = new StringBuilder();
- getUI().access(new Runnable() {
+ getUI().accessSynchronously(new Runnable() {
@Override
public void run() {
String method = VaadinServletService.getCurrentServletRequest()
diff --git a/server/src/com/vaadin/ui/ProgressBar.java b/server/src/com/vaadin/ui/ProgressBar.java
new file mode 100644
index 0000000000..3f8aab6d7c
--- /dev/null
+++ b/server/src/com/vaadin/ui/ProgressBar.java
@@ -0,0 +1,152 @@
+/*
+ * 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.ui;
+
+import com.vaadin.data.Property;
+import com.vaadin.shared.ui.progressindicator.ProgressBarState;
+
+/**
+ * Shows the current progress of a long running task.
+ * <p>
+ * The default mode is to show the current progress internally represented by a
+ * floating point value between 0 and 1 (inclusive). The progress bar can also
+ * be in an indeterminate mode showing an animation indicating that the task is
+ * running but without providing any information about the current progress.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+public class ProgressBar extends AbstractField<Float> implements
+ Property.Viewer, Property.ValueChangeListener {
+
+ /**
+ * Creates a new progress bar initially set to 0% progress.
+ */
+ public ProgressBar() {
+ this(0);
+ }
+
+ /**
+ * Creates a new progress bar with the given initial value.
+ *
+ * @param progress
+ * the initial progress value
+ */
+ public ProgressBar(float progress) {
+ setValue(Float.valueOf(progress));
+ }
+
+ /**
+ * Creates a new progress bar bound to the given data source.
+ *
+ * @param dataSource
+ * the property to bind this progress bar to
+ */
+ public ProgressBar(Property<?> dataSource) {
+ setPropertyDataSource(dataSource);
+ }
+
+ @Override
+ public void beforeClientResponse(boolean initial) {
+ super.beforeClientResponse(initial);
+
+ // Update value in state even if the property hasn't fired any event
+ getState().state = getValue();
+ }
+
+ /**
+ * Gets the value of this progress bar. The value is a <code>float</code>
+ * between 0 and 1 where 0 represents no progress at all and 1 represents
+ * fully completed.
+ *
+ * @return the current progress value
+ */
+ @Override
+ public Float getValue() {
+ return super.getValue();
+ }
+
+ /**
+ * Sets the value of this progress bar. The value is a <code>float</code>
+ * between 0 and 1 where 0 represents no progress at all and 1 represents
+ * fully completed.
+ *
+ * @param newValue
+ * the current progress value
+ */
+ @Override
+ public void setValue(Float newValue) {
+ super.setValue(newValue);
+ }
+
+ @Override
+ public Class<Float> getType() {
+ return Float.class;
+ }
+
+ @Override
+ protected ProgressBarState getState() {
+ return (ProgressBarState) super.getState();
+ }
+
+ @Override
+ protected ProgressBarState getState(boolean markAsDirty) {
+ return (ProgressBarState) super.getState(markAsDirty);
+ }
+
+ /**
+ * Sets whether or not this progress indicator is indeterminate. In
+ * indeterminate mode there is an animation indicating that the task is
+ * running but without providing any information about the current progress.
+ *
+ * @param indeterminate
+ * <code>true</code> to set to indeterminate mode; otherwise
+ * <code>false</code>
+ */
+ public void setIndeterminate(boolean indeterminate) {
+ getState().indeterminate = indeterminate;
+ }
+
+ /**
+ * Gets whether or not this progress indicator is indeterminate. In
+ * indeterminate mode there is an animation indicating that the task is
+ * running but without providing any information about the current progress.
+ *
+ * @return <code>true</code> if set to indeterminate mode; otherwise
+ * <code>false</code>
+ */
+ public boolean isIndeterminate() {
+ return getState(false).indeterminate;
+ }
+
+ /*
+ * Overridden to keep the shared state in sync with the AbstractField
+ * internal value. Should be removed once AbstractField is refactored to use
+ * shared state.
+ *
+ * See tickets #10921 and #11064.
+ */
+ @Override
+ protected void setInternalValue(Float newValue) {
+ super.setInternalValue(newValue);
+ if (newValue == null) {
+ newValue = Float.valueOf(0);
+ }
+ getState().state = newValue;
+ }
+
+} \ No newline at end of file
diff --git a/server/src/com/vaadin/ui/ProgressIndicator.java b/server/src/com/vaadin/ui/ProgressIndicator.java
index c481aa1e8f..6da18fc29d 100644
--- a/server/src/com/vaadin/ui/ProgressIndicator.java
+++ b/server/src/com/vaadin/ui/ProgressIndicator.java
@@ -17,24 +17,27 @@
package com.vaadin.ui;
import com.vaadin.data.Property;
+import com.vaadin.shared.communication.PushMode;
import com.vaadin.shared.ui.progressindicator.ProgressIndicatorServerRpc;
import com.vaadin.shared.ui.progressindicator.ProgressIndicatorState;
/**
- * <code>ProgressIndicator</code> is component that shows user state of a
- * process (like long computing or file upload)
- *
- * <code>ProgressIndicator</code> has two main modes. One for indeterminate
- * processes and other (default) for processes which progress can be measured
- *
- * May view an other property that indicates progress 0...1
+ * A {@link ProgressBar} which polls the server for updates.
+ * <p>
+ * Polling in this way is generally not recommended since there is no
+ * centralized management of when messages are sent to the server. Furthermore,
+ * polling might not be needed at all if {@link UI#setPushMode(PushMode)} or
+ * {@link UI#setPollInterval(int)} is used.
*
* @author Vaadin Ltd.
* @since 4
+ * @deprecated as of 7.1, use {@link ProgressBar} combined with
+ * {@link UI#setPushMode(PushMode)} or
+ * {@link UI#setPollInterval(int)} instead.
*/
+@Deprecated
@SuppressWarnings("serial")
-public class ProgressIndicator extends AbstractField<Float> implements
- Property.Viewer, Property.ValueChangeListener {
+public class ProgressIndicator extends ProgressBar {
private ProgressIndicatorServerRpc rpc = new ProgressIndicatorServerRpc() {
@@ -57,7 +60,7 @@ public class ProgressIndicator extends AbstractField<Float> implements
* @param value
*/
public ProgressIndicator(float value) {
- setValue(value);
+ super(value);
registerRpc(rpc);
}
@@ -68,74 +71,18 @@ public class ProgressIndicator extends AbstractField<Float> implements
* @param contentSource
*/
public ProgressIndicator(Property contentSource) {
- setPropertyDataSource(contentSource);
+ super(contentSource);
registerRpc(rpc);
}
@Override
- public void beforeClientResponse(boolean initial) {
- super.beforeClientResponse(initial);
-
- getState().state = getValue();
- }
-
- /**
- * Gets the value of the ProgressIndicator. Value of the ProgressIndicator
- * is Float between 0 and 1.
- *
- * @return the Value of the ProgressIndicator.
- * @see com.vaadin.ui.AbstractField#getValue()
- */
- @Override
- public Float getValue() {
- return super.getValue();
- }
-
- /**
- * Sets the value of the ProgressIndicator. Value of the ProgressIndicator
- * is the Float between 0 and 1.
- *
- * @param newValue
- * the New value of the ProgressIndicator.
- * @see com.vaadin.ui.AbstractField#setValue()
- */
- @Override
- public void setValue(Float newValue) {
- super.setValue(newValue);
- }
-
- /*
- * (non-Javadoc)
- *
- * @see com.vaadin.ui.AbstractField#getType()
- */
- @Override
- public Class<Float> getType() {
- return Float.class;
- }
-
- @Override
protected ProgressIndicatorState getState() {
return (ProgressIndicatorState) super.getState();
}
- /**
- * Sets whether or not the ProgressIndicator is indeterminate.
- *
- * @param indeterminate
- * true to set to indeterminate mode.
- */
- public void setIndeterminate(boolean indeterminate) {
- getState().indeterminate = indeterminate;
- }
-
- /**
- * Gets whether or not the ProgressIndicator is indeterminate.
- *
- * @return true to set to indeterminate mode.
- */
- public boolean isIndeterminate() {
- return getState().indeterminate;
+ @Override
+ protected ProgressIndicatorState getState(boolean markAsDirty) {
+ return (ProgressIndicatorState) super.getState(markAsDirty);
}
/**
@@ -154,22 +101,6 @@ public class ProgressIndicator extends AbstractField<Float> implements
* @return the interval in milliseconds.
*/
public int getPollingInterval() {
- return getState().pollingInterval;
- }
-
- /*
- * Overridden to keep the shared state in sync with the AbstractField
- * internal value. Should be removed once AbstractField is refactored to use
- * shared state.
- *
- * See tickets #10921 and #11064.
- */
- @Override
- protected void setInternalValue(Float newValue) {
- super.setInternalValue(newValue);
- if (newValue == null) {
- newValue = 0.0f;
- }
- getState().state = newValue;
+ return getState(false).pollingInterval;
}
}
diff --git a/server/src/com/vaadin/ui/PushConfiguration.java b/server/src/com/vaadin/ui/PushConfiguration.java
new file mode 100644
index 0000000000..a592b39bef
--- /dev/null
+++ b/server/src/com/vaadin/ui/PushConfiguration.java
@@ -0,0 +1,282 @@
+/*
+ * 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.ui;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Collections;
+
+import com.vaadin.server.VaadinSession;
+import com.vaadin.shared.communication.PushMode;
+import com.vaadin.shared.ui.ui.Transport;
+import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
+
+/**
+ * Provides method for configuring the push channel.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+public interface PushConfiguration extends Serializable {
+
+ /**
+ * Returns the mode of bidirectional ("push") communication that is used.
+ *
+ * @return The push mode.
+ */
+ public PushMode getPushMode();
+
+ /**
+ * Sets the mode of bidirectional ("push") communication that should be
+ * used.
+ * <p>
+ * Add-on developers should note that this method is only meant for the
+ * application developer. An add-on should not set the push mode directly,
+ * rather instruct the user to set it.
+ * </p>
+ *
+ * @param pushMode
+ * The push mode to use.
+ *
+ * @throws IllegalArgumentException
+ * if the argument is null.
+ * @throws IllegalStateException
+ * if push support is not available.
+ */
+ public void setPushMode(PushMode pushMode);
+
+ /**
+ * Returns the primary transport type for push.
+ * <p>
+ * Note that if you set the transport type using
+ * {@link #setParameter(String, String)} to an unsupported type this method
+ * will return null. Supported types are defined by {@link Transport}.
+ *
+ * @return The primary transport type
+ */
+ public Transport getTransport();
+
+ /**
+ * Sets the primary transport type for push.
+ * <p>
+ * Note that the new transport type will not be used until the push channel
+ * is disconnected and reconnected if already active.
+ *
+ * @param transport
+ * The primary transport type
+ */
+ public void setTransport(Transport transport);
+
+ /**
+ * Returns the fallback transport type for push.
+ * <p>
+ * Note that if you set the transport type using
+ * {@link #setParameter(String, String)} to an unsupported type this method
+ * will return null. Supported types are defined by {@link Transport}.
+ *
+ * @return The fallback transport type
+ */
+ public Transport getFallbackTransport();
+
+ /**
+ * Sets the fallback transport type for push.
+ * <p>
+ * Note that the new transport type will not be used until the push channel
+ * is disconnected and reconnected if already active.
+ *
+ * @param fallbackTransport
+ * The fallback transport type
+ */
+ public void setFallbackTransport(Transport fallbackTransport);
+
+ /**
+ * Returns the given parameter, if set.
+ * <p>
+ * This method provides low level access to push parameters and is typically
+ * not needed for normal application development.
+ *
+ * @since 7.1
+ * @param parameter
+ * The parameter name
+ * @return The parameter value or null if not set
+ */
+ public String getParameter(String parameter);
+
+ /**
+ * Returns the parameters which have been defined.
+ *
+ * @since 7.1
+ * @return A collection of parameter names
+ */
+ public Collection<String> getParameterNames();
+
+ /**
+ * Sets the given parameter.
+ * <p>
+ * This method provides low level access to push parameters and is typically
+ * not needed for normal application development.
+ *
+ * @since 7.1
+ * @param parameter
+ * The parameter name
+ * @param value
+ * The value
+ */
+ public void setParameter(String parameter, String value);
+
+}
+
+class PushConfigurationImpl implements PushConfiguration {
+ private UI ui;
+
+ public PushConfigurationImpl(UI ui) {
+ this.ui = ui;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.PushConfiguration#getPushMode()
+ */
+ @Override
+ public PushMode getPushMode() {
+ return getState(false).mode;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.PushConfiguration#setPushMode(com.vaadin.shared.communication
+ * .PushMode)
+ */
+ @Override
+ public void setPushMode(PushMode pushMode) {
+ if (pushMode == null) {
+ throw new IllegalArgumentException("Push mode cannot be null");
+ }
+
+ if (pushMode.isEnabled()) {
+ VaadinSession session = ui.getSession();
+ if (session != null && !session.getService().ensurePushAvailable()) {
+ throw new IllegalStateException(
+ "Push is not available. See previous log messages for more information.");
+ }
+ }
+
+ /*
+ * Client-side will open a new connection or disconnect the old
+ * connection, so there's nothing more to do on the server at this
+ * point.
+ */
+ getState().mode = pushMode;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.PushConfiguration#getTransport()
+ */
+ @Override
+ public Transport getTransport() {
+ try {
+ return Transport
+ .valueOf(getParameter(PushConfigurationState.TRANSPORT_PARAM));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.PushConfiguration#setTransport(com.vaadin.shared.ui.ui.
+ * Transport)
+ */
+ @Override
+ public void setTransport(Transport transport) {
+ setParameter(PushConfigurationState.TRANSPORT_PARAM,
+ transport.getIdentifier());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.PushConfiguration#getFallbackTransport()
+ */
+ @Override
+ public Transport getFallbackTransport() {
+ try {
+ return Transport
+ .valueOf(getParameter(PushConfigurationState.FALLBACK_TRANSPORT_PARAM));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see
+ * com.vaadin.ui.PushConfiguration#setFallbackTransport(com.vaadin.shared
+ * .ui.ui.Transport)
+ */
+ @Override
+ public void setFallbackTransport(Transport fallbackTransport) {
+ setParameter(PushConfigurationState.FALLBACK_TRANSPORT_PARAM,
+ fallbackTransport.getIdentifier());
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.PushConfiguration#getParameter(java.lang.String)
+ */
+ @Override
+ public String getParameter(String parameter) {
+ return getState(false).parameters.get(parameter);
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.PushConfiguration#setParameter(java.lang.String,
+ * java.lang.String)
+ */
+ @Override
+ public void setParameter(String parameter, String value) {
+ getState().parameters.put(parameter, value);
+
+ }
+
+ private PushConfigurationState getState() {
+ return ui.getState().pushConfiguration;
+ }
+
+ private PushConfigurationState getState(boolean markAsDirty) {
+ return ui.getState(markAsDirty).pushConfiguration;
+ }
+
+ @Override
+ public Collection<String> getParameterNames() {
+ return Collections
+ .unmodifiableCollection(ui.getState(false).pushConfiguration.parameters
+ .keySet());
+ }
+
+}
diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java
index 0a4ed7c491..9135151089 100644
--- a/server/src/com/vaadin/ui/UI.java
+++ b/server/src/com/vaadin/ui/UI.java
@@ -21,7 +21,10 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
+import java.util.List;
import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.logging.Logger;
import com.vaadin.event.Action;
import com.vaadin.event.Action.Handler;
@@ -29,6 +32,10 @@ import com.vaadin.event.ActionManager;
import com.vaadin.event.MouseEvents.ClickEvent;
import com.vaadin.event.MouseEvents.ClickListener;
import com.vaadin.navigator.Navigator;
+import com.vaadin.server.ClientConnector;
+import com.vaadin.server.ComponentSizeValidator;
+import com.vaadin.server.ComponentSizeValidator.InvalidLayout;
+import com.vaadin.server.LocaleService;
import com.vaadin.server.Page;
import com.vaadin.server.PaintException;
import com.vaadin.server.PaintTarget;
@@ -38,15 +45,18 @@ import com.vaadin.server.VaadinService;
import com.vaadin.server.VaadinServlet;
import com.vaadin.server.VaadinSession;
import com.vaadin.server.communication.PushConnection;
+import com.vaadin.shared.Connector;
import com.vaadin.shared.EventId;
import com.vaadin.shared.MouseEventDetails;
-import com.vaadin.shared.communication.PushMode;
+import com.vaadin.shared.ui.ui.DebugWindowClientRpc;
+import com.vaadin.shared.ui.ui.DebugWindowServerRpc;
import com.vaadin.shared.ui.ui.ScrollClientRpc;
import com.vaadin.shared.ui.ui.UIClientRpc;
import com.vaadin.shared.ui.ui.UIConstants;
import com.vaadin.shared.ui.ui.UIServerRpc;
import com.vaadin.shared.ui.ui.UIState;
import com.vaadin.ui.Component.Focusable;
+import com.vaadin.util.ConnectorHelper;
import com.vaadin.util.CurrentInstance;
/**
@@ -86,7 +96,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
/**
* The application to which this UI belongs
*/
- private VaadinSession session;
+ private volatile VaadinSession session;
/**
* List of windows in this UI.
@@ -159,6 +169,40 @@ public abstract class UI extends AbstractSingleComponentContainer implements
*/
}
};
+ private DebugWindowServerRpc debugRpc = new DebugWindowServerRpc() {
+ @Override
+ public void showServerDebugInfo(Connector connector) {
+ String info = ConnectorHelper
+ .getDebugInformation((ClientConnector) connector);
+ getLogger().info(info);
+ }
+
+ @Override
+ public void analyzeLayouts() {
+ // TODO Move to client side
+ List<InvalidLayout> invalidSizes = ComponentSizeValidator
+ .validateLayouts(UI.this);
+ StringBuilder json = new StringBuilder();
+ json.append("{\"invalidLayouts\":");
+ json.append("[");
+
+ if (invalidSizes != null) {
+ boolean first = true;
+ for (InvalidLayout invalidSize : invalidSizes) {
+ if (!first) {
+ json.append(",");
+ } else {
+ first = false;
+ }
+ invalidSize.reportErrors(json, System.err);
+ }
+ }
+ json.append("]}");
+ getRpcProxy(DebugWindowClientRpc.class).reportLayoutProblems(
+ json.toString());
+ }
+
+ };
/**
* Timestamp keeping track of the last heartbeat of this UI. Updated to the
@@ -171,6 +215,8 @@ public abstract class UI extends AbstractSingleComponentContainer implements
private TooltipConfiguration tooltipConfiguration = new TooltipConfigurationImpl(
this);
+ private PushConfiguration pushConfiguration = new PushConfigurationImpl(
+ this);
/**
* Creates a new empty UI without a caption. The content of the UI must be
@@ -191,6 +237,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
*/
public UI(Component content) {
registerRpc(rpc);
+ registerRpc(debugRpc);
setSizeFull();
setContent(content);
}
@@ -418,7 +465,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
throw new NullPointerException("Argument must not be null");
}
- if (window.getUI() != null && window.getUI().getSession() != null) {
+ if (window.isAttached()) {
throw new IllegalArgumentException(
"Window is already attached to an application.");
}
@@ -493,6 +540,9 @@ public abstract class UI extends AbstractSingleComponentContainer implements
private boolean hasPendingPush = false;
+ private LocaleService localeService = new LocaleService(this,
+ getState(false).localeServiceState);
+
/**
* This method is used by Component.Focusable objects to request focus to
* themselves. Focus renders must be handled at window level (instead of
@@ -1051,6 +1101,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
@Override
public void attach() {
super.attach();
+ getLocaleService().addLocale(getLocale());
}
/**
@@ -1098,24 +1149,30 @@ public abstract class UI extends AbstractSingleComponentContainer implements
}
/**
- * Provides exclusive access to this UI from outside a request handling
- * thread.
- * <p>
- * The given runnable is executed while holding the session lock to ensure
- * exclusive access to this UI and its session. The UI and related thread
- * locals are set properly before executing the runnable.
- * </p>
+ * Locks the session of this UI and runs the provided Runnable right away.
* <p>
- * RPC handlers for components inside this UI do not need this method as the
- * session is automatically locked by the framework during request handling.
+ * It is generally recommended to use {@link #access(Runnable)} instead of
+ * this method for accessing a session from a different thread as
+ * {@link #access(Runnable)} can be used while holding the lock of another
+ * session. To avoid causing deadlocks, this methods throws an exception if
+ * it is detected than another session is also locked by the current thread.
* </p>
* <p>
- * Note that calling this method while another session is locked by the
- * current thread will cause an exception. This is to prevent deadlock
- * situations when two threads have locked one session each and are both
- * waiting for the lock for the other session.
+ * This method behaves differently than {@link #access(Runnable)} in some
+ * situations:
+ * <ul>
+ * <li>If the current thread is currently holding the lock of the session,
+ * {@link #accessSynchronously(Runnable)} runs the task right away whereas
+ * {@link #access(Runnable)} defers the task to a later point in time.</li>
+ * <li>If some other thread is currently holding the lock for the session,
+ * {@link #accessSynchronously(Runnable)} blocks while waiting for the lock
+ * to be available whereas {@link #access(Runnable)} defers the task to a
+ * later point in time.</li>
+ * </ul>
* </p>
*
+ * @since 7.1
+ *
* @param runnable
* the runnable which accesses the UI
* @throws UIDetachedException
@@ -1124,11 +1181,11 @@ public abstract class UI extends AbstractSingleComponentContainer implements
* @throws IllegalStateException
* if the current thread holds the lock for another session
*
- * @see #getCurrent()
- * @see VaadinSession#access(Runnable)
- * @see VaadinSession#lock()
+ * @see #access(Runnable)
+ * @see VaadinSession#accessSynchronously(Runnable)
*/
- public void access(Runnable runnable) throws UIDetachedException {
+ public void accessSynchronously(Runnable runnable)
+ throws UIDetachedException {
Map<Class<?>, CurrentInstance> old = null;
VaadinSession session = getSession();
@@ -1146,24 +1203,76 @@ public abstract class UI extends AbstractSingleComponentContainer implements
// acquired the lock.
throw new UIDetachedException();
}
- old = CurrentInstance.setThreadLocals(this);
+ old = CurrentInstance.setCurrent(this);
runnable.run();
} finally {
session.unlock();
if (old != null) {
- CurrentInstance.restoreThreadLocals(old);
+ CurrentInstance.restoreInstances(old);
}
}
}
/**
- * @deprecated As of 7.1.0.beta1, use {@link #access(Runnable)} instead.
- * This method will be removed before the final 7.1.0 release.
+ * Provides exclusive access to this UI from outside a request handling
+ * thread.
+ * <p>
+ * The given runnable is executed while holding the session lock to ensure
+ * exclusive access to this UI. If the session is not locked, the lock will
+ * be acquired and the runnable is run right away. If the session is
+ * currently locked, the runnable will be run before that lock is released.
+ * </p>
+ * <p>
+ * RPC handlers for components inside this UI do not need to use this method
+ * as the session is automatically locked by the framework during RPC
+ * handling.
+ * </p>
+ * <p>
+ * Please note that the runnable might be invoked on a different thread or
+ * later on the current thread, which means that custom thread locals might
+ * not have the expected values when the runnable is executed. Inheritable
+ * values in {@link CurrentInstance} will have the same values as when this
+ * method was invoked. {@link UI#getCurrent()},
+ * {@link VaadinSession#getCurrent()} and {@link VaadinService#getCurrent()}
+ * are set according to this UI before executing the runnable.
+ * Non-inheritable CurrentInstance values including
+ * {@link VaadinService#getCurrentRequest()} and
+ * {@link VaadinService#getCurrentResponse()} will not be defined.
+ * </p>
+ * <p>
+ * The returned future can be used to check for task completion and to
+ * cancel the task.
+ * </p>
+ *
+ * @see #getCurrent()
+ * @see #accessSynchronously(Runnable)
+ * @see VaadinSession#access(Runnable)
+ * @see VaadinSession#lock()
+ *
+ * @since 7.1
+ *
+ * @param runnable
+ * the runnable which accesses the UI
+ * @throws UIDetachedException
+ * if the UI is not attached to a session (and locking can
+ * therefore not be done)
+ * @return a future that can be used to check for task completion and to
+ * cancel the task
*/
- @Deprecated
- public void runSafely(Runnable runnable) throws UIDetachedException {
- access(runnable);
+ public Future<Void> access(final Runnable runnable) {
+ VaadinSession session = getSession();
+
+ if (session == null) {
+ throw new UIDetachedException();
+ }
+
+ return session.access(new Runnable() {
+ @Override
+ public void run() {
+ accessSynchronously(runnable);
+ }
+ });
}
/**
@@ -1204,12 +1313,20 @@ public abstract class UI extends AbstractSingleComponentContainer implements
VaadinSession session = getSession();
if (session != null) {
assert session.hasLock();
+
+ /*
+ * Purge the pending access queue as it might mark a connector as
+ * dirty when the push would otherwise be ignored because there are
+ * no changes to push.
+ */
+ session.getService().runPendingAccessTasks(session);
+
if (!getConnectorTracker().hasDirtyConnectors()) {
// Do not push if there is nothing to push
return;
}
- if (!getPushMode().isEnabled()) {
+ if (!getPushConfiguration().getPushMode().isEnabled()) {
throw new IllegalStateException("Push not enabled");
}
@@ -1237,7 +1354,7 @@ public abstract class UI extends AbstractSingleComponentContainer implements
*/
public void setPushConnection(PushConnection pushConnection) {
// If pushMode is disabled then there should never be a pushConnection
- assert (getPushMode().isEnabled() || pushConnection == null);
+ assert (getPushConfiguration().getPushMode().isEnabled() || pushConnection == null);
if (pushConnection == this.pushConnection) {
return;
@@ -1286,51 +1403,13 @@ public abstract class UI extends AbstractSingleComponentContainer implements
}
/**
- * Returns the mode of bidirectional ("push") communication that is used in
- * this UI.
- *
- * @return The push mode.
- */
- public PushMode getPushMode() {
- return getState(false).pushMode;
- }
-
- /**
- * Sets the mode of bidirectional ("push") communication that should be used
- * in this UI.
- * <p>
- * Add-on developers should note that this method is only meant for the
- * application developer. An add-on should not set the push mode directly,
- * rather instruct the user to set it.
- * </p>
- *
- * @param pushMode
- * The push mode to use.
+ * Retrieves the object used for configuring the push channel.
*
- * @throws IllegalArgumentException
- * if the argument is null.
- * @throws IllegalStateException
- * if push support is not available.
+ * @since 7.1
+ * @return The instance used for push configuration
*/
- public void setPushMode(PushMode pushMode) {
- if (pushMode == null) {
- throw new IllegalArgumentException("Push mode cannot be null");
- }
-
- if (pushMode.isEnabled()) {
- VaadinSession session = getSession();
- if (session != null && !session.getService().ensurePushAvailable()) {
- throw new IllegalStateException(
- "Push is not available. See previous log messages for more information.");
- }
- }
-
- /*
- * Client-side will open a new connection or disconnect the old
- * connection, so there's nothing more to do on the server at this
- * point.
- */
- getState().pushMode = pushMode;
+ public PushConfiguration getPushConfiguration() {
+ return pushConfiguration;
}
/**
@@ -1357,4 +1436,19 @@ public abstract class UI extends AbstractSingleComponentContainer implements
public void setOverlayContainerLabel(String overlayContainerLabel) {
getState().overlayContainerLabel = overlayContainerLabel;
}
+
+ /**
+ * Returns the locale service which handles transmission of Locale data to
+ * the client.
+ *
+ * @since 7.1
+ * @return The LocaleService for this UI
+ */
+ public LocaleService getLocaleService() {
+ return localeService;
+ }
+
+ private static Logger getLogger() {
+ return Logger.getLogger(UI.class.getName());
+ }
}
diff --git a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java
index 2902585f56..de8c5db195 100644
--- a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java
+++ b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java
@@ -95,7 +95,7 @@ public class ColorPickerHistory extends CustomComponent implements
@SuppressWarnings("unchecked")
private ArrayBlockingQueue<Color> getColorHistory() {
- if (getSession() != null) {
+ if (isAttached()) {
Object colorHistory = getSession().getAttribute(
"colorPickerHistory");
if (colorHistory instanceof ArrayBlockingQueue<?>) {
diff --git a/server/src/com/vaadin/util/ConnectorHelper.java b/server/src/com/vaadin/util/ConnectorHelper.java
new file mode 100644
index 0000000000..e698e9222a
--- /dev/null
+++ b/server/src/com/vaadin/util/ConnectorHelper.java
@@ -0,0 +1,101 @@
+/*
+ * 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.util.LinkedList;
+
+import com.vaadin.server.ClientConnector;
+import com.vaadin.ui.Component;
+
+/**
+ * Provides various helper methods for connectors. Meant for internal use.
+ *
+ * @since 7.1
+ * @author Vaadin Ltd
+ */
+public class ConnectorHelper {
+
+ /**
+ * Creates a string containing debug info for the connector
+ *
+ * @since 7.1
+ * @param connector
+ * The connector to print debug info about
+ * @return A string with debug information
+ */
+ public static String getDebugInformation(ClientConnector connector) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("*** Debug details of a connector: *** \n");
+ sb.append("Type: ");
+ sb.append(connector.getClass().getName());
+ sb.append("\nId:");
+ sb.append(connector.getConnectorId());
+ if (connector instanceof Component) {
+ Component component = (Component) connector;
+ if (component.getCaption() != null) {
+ sb.append("\nCaption:");
+ sb.append(component.getCaption());
+ }
+ }
+ writeHierarchyInformation(connector, sb);
+ return sb.toString();
+ }
+
+ /**
+ * Creates a string containing hierarchy information for the connector
+ *
+ * @since 7.1
+ * @param connector
+ * The connector to get hierarchy information for
+ * @param builder
+ * The StringBuilder where the information should be written
+ */
+ public static void writeHierarchyInformation(ClientConnector connector,
+ StringBuilder builder) {
+ LinkedList<ClientConnector> h = new LinkedList<ClientConnector>();
+ h.add(connector);
+ ClientConnector parent = connector.getParent();
+ while (parent != null) {
+ h.addFirst(parent);
+ parent = parent.getParent();
+ }
+
+ builder.append("\nConnector hierarchy:\n");
+
+ int l = 0;
+ for (ClientConnector connector2 : h) {
+ if (l != 0) {
+ builder.append("\n");
+ for (int i = 0; i < l; i++) {
+ builder.append(" ");
+ }
+ }
+ l++;
+ Class<? extends ClientConnector> connectorClass = connector2
+ .getClass();
+ Class<?> topClass = connectorClass;
+ while (topClass.getEnclosingClass() != null) {
+ topClass = topClass.getEnclosingClass();
+ }
+ builder.append(connectorClass.getName());
+ builder.append("(");
+ builder.append(topClass.getSimpleName());
+ builder.append(".java:1)");
+ }
+ }
+
+}
diff --git a/server/src/com/vaadin/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java
index 60489d596e..b97bab3d8a 100644
--- a/server/src/com/vaadin/util/CurrentInstance.java
+++ b/server/src/com/vaadin/util/CurrentInstance.java
@@ -17,53 +17,41 @@
package com.vaadin.util;
import java.io.Serializable;
+import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import com.vaadin.server.VaadinPortlet;
-import com.vaadin.server.VaadinPortletService;
import com.vaadin.server.VaadinRequest;
import com.vaadin.server.VaadinResponse;
import com.vaadin.server.VaadinService;
-import com.vaadin.server.VaadinServlet;
-import com.vaadin.server.VaadinServletService;
import com.vaadin.server.VaadinSession;
import com.vaadin.ui.UI;
/**
- * Keeps track of various thread local instances used by the framework.
+ * Keeps track of various current instances for the current thread. All the
+ * instances are automatically cleared after handling a request from the client
+ * to avoid leaking memory. The inheritable values are also maintained when
+ * execution is moved to another thread, both when a new thread is created and
+ * when {@link VaadinSession#access(Runnable)} or {@link UI#access(Runnable)} is
+ * used.
* <p>
* Currently the framework uses the following instances:
* </p>
* <p>
- * Inheritable: {@link UI}, {@link VaadinPortlet}, {@link VaadinService},
- * {@link VaadinServlet}, {@link VaadinSession}.
+ * Inheritable: {@link UI}, {@link VaadinService}, {@link VaadinSession}.
* </p>
* <p>
* Non-inheritable: {@link VaadinRequest}, {@link VaadinResponse}.
* </p>
*
* @author Vaadin Ltd
- * @version @VERSION@
* @since 7.0.0
*/
public class CurrentInstance implements Serializable {
private final Object instance;
private final boolean inheritable;
- private static boolean portletAvailable = false;
- {
- try {
- /*
- * VaadinPortlet depends on portlet API which is available only if
- * running in a portal.
- */
- portletAvailable = (VaadinPortlet.class.getName() != null);
- } catch (Throwable t) {
- }
- }
-
private static InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() {
@Override
protected Map<Class<?>, CurrentInstance> childValue(
@@ -129,7 +117,9 @@ public class CurrentInstance implements Serializable {
/**
* Sets the current inheritable instance of the given type. A current
- * instance that is inheritable will be available for child threads.
+ * instance that is inheritable will be available for child threads and in
+ * code run by {@link VaadinSession#access(Runnable)} and
+ * {@link UI#access(Runnable)}.
*
* @see #set(Class, Object)
* @see InheritableThreadLocal
@@ -184,13 +174,15 @@ public class CurrentInstance implements Serializable {
}
/**
- * Restores the given thread locals to the given values. Note that this
- * should only be used internally to restore Vaadin classes.
+ * Restores the given instances to the given values. Note that this should
+ * only be used internally to restore Vaadin classes.
+ *
+ * @since 7.1
*
* @param old
- * A Class -> Object map to set as thread locals
+ * A Class -> CurrentInstance map to set as current instances
*/
- public static void restoreThreadLocals(Map<Class<?>, CurrentInstance> old) {
+ public static void restoreInstances(Map<Class<?>, CurrentInstance> old) {
for (Class c : old.keySet()) {
CurrentInstance ci = old.get(c);
set(c, ci.instance, ci.inheritable);
@@ -198,30 +190,66 @@ public class CurrentInstance implements Serializable {
}
/**
- * Sets thread locals for the UI and all related classes
+ * Gets the currently set instances so that they can later be restored using
+ * {@link #restoreInstances(Map)}.
+ *
+ * @since 7.1
+ *
+ * @param onlyInheritable
+ * <code>true</code> if only the inheritable instances should be
+ * included; <code>false</code> to get all instances.
+ * @return a map containing the current instances
+ */
+ public static Map<Class<?>, CurrentInstance> getInstances(
+ boolean onlyInheritable) {
+ Map<Class<?>, CurrentInstance> map = instances.get();
+ if (map == null) {
+ return Collections.emptyMap();
+ } else {
+ Map<Class<?>, CurrentInstance> copy = new HashMap<Class<?>, CurrentInstance>();
+ for (Class<?> c : map.keySet()) {
+ CurrentInstance ci = map.get(c);
+ if (ci.inheritable || !onlyInheritable) {
+ copy.put(c, ci);
+ }
+ }
+ return copy;
+ }
+ }
+
+ /**
+ * Sets current instances for the UI and all related classes. The previously
+ * defined values can be restored by passing the returned map to
+ * {@link #restoreInstances(Map)}.
+ *
+ * @since 7.1
*
* @param ui
* The UI
- * @return A map containing the old values of the thread locals this method
+ * @return A map containing the old values of the instances that this method
* updated.
*/
- public static Map<Class<?>, CurrentInstance> setThreadLocals(UI ui) {
+ 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));
UI.setCurrent(ui);
- old.putAll(setThreadLocals(ui.getSession()));
+ old.putAll(setCurrent(ui.getSession()));
return old;
}
/**
- * Sets thread locals for the {@link VaadinSession} and all related classes
+ * Sets current instances for the {@link VaadinSession} and all related
+ * classes. The previously defined values can be restored by passing the
+ * returned map to {@link #restoreInstances(Map)}.
+ *
+ * @since 7.1
*
* @param session
* The VaadinSession
- * @return A map containing the old values of the thread locals this method
+ * @return A map containing the old values of the instances this method
* updated.
*/
- public static Map<Class<?>, CurrentInstance> setThreadLocals(
+ public static Map<Class<?>, CurrentInstance> setCurrent(
VaadinSession session) {
Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>();
old.put(VaadinSession.class,
@@ -236,18 +264,6 @@ public class CurrentInstance implements Serializable {
VaadinSession.setCurrent(session);
VaadinService.setCurrent(service);
- if (service instanceof VaadinServletService) {
- old.put(VaadinServlet.class,
- new CurrentInstance(VaadinServlet.getCurrent(), true));
- VaadinServlet.setCurrent(((VaadinServletService) service)
- .getServlet());
- } else if (portletAvailable && service instanceof VaadinPortletService) {
- old.put(VaadinPortlet.class,
- new CurrentInstance(VaadinPortlet.getCurrent(), true));
- VaadinPortlet.setCurrent(((VaadinPortletService) service)
- .getPortlet());
- }
-
return old;
}
}