summaryrefslogtreecommitdiffstats
path: root/server/src/com/vaadin
diff options
context:
space:
mode:
Diffstat (limited to 'server/src/com/vaadin')
-rw-r--r--server/src/com/vaadin/data/RpcDataProviderExtension.java89
-rw-r--r--server/src/com/vaadin/server/Constants.java5
-rw-r--r--server/src/com/vaadin/server/DefaultDeploymentConfiguration.java19
-rw-r--r--server/src/com/vaadin/server/DeploymentConfiguration.java10
-rw-r--r--server/src/com/vaadin/server/ServletPortletHelper.java3
-rw-r--r--server/src/com/vaadin/server/VaadinService.java48
-rw-r--r--server/src/com/vaadin/server/communication/AtmospherePushConnection.java27
-rw-r--r--server/src/com/vaadin/server/communication/PushHandler.java35
-rw-r--r--server/src/com/vaadin/server/communication/UIInitHandler.java5
-rw-r--r--server/src/com/vaadin/ui/Flash.java10
-rw-r--r--server/src/com/vaadin/ui/Grid.java15
-rw-r--r--server/src/com/vaadin/ui/JavaScriptFunction.java7
-rw-r--r--server/src/com/vaadin/ui/PushConfiguration.java47
-rw-r--r--server/src/com/vaadin/ui/Slider.java18
-rw-r--r--server/src/com/vaadin/ui/UI.java7
-rw-r--r--server/src/com/vaadin/ui/declarative/Design.java113
-rw-r--r--server/src/com/vaadin/ui/declarative/DesignContext.java51
17 files changed, 380 insertions, 129 deletions
diff --git a/server/src/com/vaadin/data/RpcDataProviderExtension.java b/server/src/com/vaadin/data/RpcDataProviderExtension.java
index 354bfe336c..991cb0537d 100644
--- a/server/src/com/vaadin/data/RpcDataProviderExtension.java
+++ b/server/src/com/vaadin/data/RpcDataProviderExtension.java
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.LinkedHashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -325,10 +326,10 @@ public class RpcDataProviderExtension extends AbstractExtension {
*/
private class ActiveRowHandler implements Serializable {
/**
- * A map from itemId to the value change listener used for all of its
+ * A map from index to the value change listener used for all of column
* properties
*/
- private final Map<Object, GridValueChangeListener> valueChangeListeners = new HashMap<Object, GridValueChangeListener>();
+ private final Map<Integer, GridValueChangeListener> valueChangeListeners = new HashMap<Integer, GridValueChangeListener>();
/**
* The currently active range. Practically, it's the range of row
@@ -388,36 +389,27 @@ public class RpcDataProviderExtension extends AbstractExtension {
}
private void addValueChangeListeners(Range range) {
- for (int i = range.getStart(); i < range.getEnd(); i++) {
+ for (Integer i = range.getStart(); i < range.getEnd(); i++) {
final Object itemId = container.getIdByIndex(i);
final Item item = container.getItem(itemId);
- if (valueChangeListeners.containsKey(itemId)) {
- /*
- * This might occur when items are removed from above the
- * viewport, the escalator scrolls up to compensate, but the
- * same items remain in the view: It looks as if one row was
- * scrolled, when in fact the whole viewport was shifted up.
- */
- continue;
- }
+ assert valueChangeListeners.get(i) == null : "Overwriting existing listener";
GridValueChangeListener listener = new GridValueChangeListener(
itemId, item);
- valueChangeListeners.put(itemId, listener);
+ valueChangeListeners.put(i, listener);
}
}
private void removeValueChangeListeners(Range range) {
- for (int i = range.getStart(); i < range.getEnd(); i++) {
- final Object itemId = container.getIdByIndex(i);
+ for (Integer i = range.getStart(); i < range.getEnd(); i++) {
final GridValueChangeListener listener = valueChangeListeners
- .remove(itemId);
+ .remove(i);
- if (listener != null) {
- listener.removeListener();
- }
+ assert listener != null : "Trying to remove nonexisting listener";
+
+ listener.removeListener();
}
}
@@ -478,12 +470,16 @@ public class RpcDataProviderExtension extends AbstractExtension {
*/
public void insertRows(int firstIndex, int count) {
if (firstIndex < activeRange.getStart()) {
+ moveListeners(activeRange, count);
activeRange = activeRange.offsetBy(count);
} else if (firstIndex < activeRange.getEnd()) {
- final Range deprecatedRange = Range.withLength(
- activeRange.getEnd(), count);
- removeValueChangeListeners(deprecatedRange);
-
+ int end = activeRange.getEnd();
+ // Move rows from first added index by count
+ Range movedRange = Range.between(firstIndex, end);
+ moveListeners(movedRange, count);
+ // Remove excess listeners from extra rows
+ removeValueChangeListeners(Range.withLength(end, count));
+ // Add listeners for new rows
final Range freshRange = Range.withLength(firstIndex, count);
addValueChangeListeners(freshRange);
} else {
@@ -509,23 +505,52 @@ public class RpcDataProviderExtension extends AbstractExtension {
public void removeRows(int firstIndex, int count) {
Range removed = Range.withLength(firstIndex, count);
if (removed.intersects(activeRange)) {
- final Range deprecated = removed.restrictTo(activeRange);
- for (int i = deprecated.getStart(); i < deprecated.getEnd(); ++i) {
- Object itemId = keyMapper.itemIdAtIndex(i);
- // Item doesn't exist anymore.
- valueChangeListeners.remove(itemId);
- }
+ final Range[] deprecated = activeRange.partitionWith(removed);
+ // Remove the listeners that are no longer existing
+ removeValueChangeListeners(deprecated[1]);
+ // Move remaining listeners to fill the listener map correctly
+ moveListeners(deprecated[2], -deprecated[1].length());
activeRange = Range.withLength(activeRange.getStart(),
- activeRange.length() - deprecated.length());
+ activeRange.length() - deprecated[1].length());
+
} else {
if (removed.getEnd() < activeRange.getStart()) {
/* firstIndex < lastIndex < start */
+ moveListeners(activeRange, -count);
activeRange = activeRange.offsetBy(-count);
}
/* else: end <= firstIndex, no need to do anything */
}
}
+
+ /**
+ * Moves value change listeners in map with given index range by count
+ */
+ private void moveListeners(Range movedRange, int diff) {
+ if (diff < 0) {
+ for (Integer i = movedRange.getStart(); i < movedRange.getEnd(); ++i) {
+ moveListener(i, i + diff);
+ }
+ } else if (diff > 0) {
+ for (Integer i = movedRange.getEnd() - 1; i >= movedRange
+ .getStart(); --i) {
+ moveListener(i, i + diff);
+ }
+ } else {
+ // diff == 0 should not happen. If it does, should be no-op
+ return;
+ }
+ }
+
+ private void moveListener(Integer oldIndex, Integer newIndex) {
+ assert valueChangeListeners.get(newIndex) == null : "Overwriting existing listener";
+
+ GridValueChangeListener listener = valueChangeListeners
+ .remove(oldIndex);
+ assert listener != null : "Moving nonexisting listener.";
+ valueChangeListeners.put(newIndex, listener);
+ }
}
/**
@@ -663,7 +688,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
* taking all the corner cases into account.
*/
- Map<Object, GridValueChangeListener> listeners = activeRowHandler.valueChangeListeners;
+ Map<Integer, GridValueChangeListener> listeners = activeRowHandler.valueChangeListeners;
for (GridValueChangeListener listener : listeners.values()) {
listener.removeListener();
}
@@ -691,7 +716,7 @@ public class RpcDataProviderExtension extends AbstractExtension {
private CellReference cellReference;
/** Set of updated item ids */
- private Set<Object> updatedItemIds = new HashSet<Object>();
+ private Set<Object> updatedItemIds = new LinkedHashSet<Object>();
/**
* Queued RPC calls for adding and removing rows. Queue will be handled in
diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java
index 8036490333..b9a43a98de 100644
--- a/server/src/com/vaadin/server/Constants.java
+++ b/server/src/com/vaadin/server/Constants.java
@@ -67,7 +67,7 @@ public interface Constants {
// Keep the version number in sync with push/build.xml and other locations
// listed in that file
- static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "2.2.4.vaadin2";
+ static final String REQUIRED_ATMOSPHERE_RUNTIME_VERSION = "2.2.4.vaadin4";
static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n"
+ "=================================================================\n"
@@ -132,11 +132,12 @@ public interface Constants {
static final String SERVLET_PARAMETER_RESOURCE_CACHE_TIME = "resourceCacheTime";
static final String SERVLET_PARAMETER_HEARTBEAT_INTERVAL = "heartbeatInterval";
static final String SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS = "closeIdleSessions";
- static final String SERVLET_PARAMETER_PUSH_MODE = "pushMode";
static final String SERVLET_PARAMETER_UI_PROVIDER = "UIProvider";
static final String SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING = "legacyPropertyToString";
static final String SERVLET_PARAMETER_SYNC_ID_CHECK = "syncIdCheck";
static final String SERVLET_PARAMETER_SENDURLSASPARAMETERS = "sendUrlsAsParameters";
+ static final String SERVLET_PARAMETER_PUSH_MODE = "pushMode";
+ static final String SERVLET_PARAMETER_PUSH_PATH = "pushPath";
// Configurable parameter names
static final String PARAMETER_VAADIN_RESOURCES = "Resources";
diff --git a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java
index b26e048431..5402979be8 100644
--- a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java
+++ b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java
@@ -61,6 +61,13 @@ public class DefaultDeploymentConfiguration extends
public static final boolean DEFAULT_SEND_URLS_AS_PARAMETERS = true;
+ /**
+ * Default value for {@link #getPushPath()} = {@value} .
+ *
+ * @since 7.4.1
+ */
+ public static final String DEFAULT_PUSH_PATH = "PUSH";
+
private final Properties initParameters;
private boolean productionMode;
private boolean xsrfProtectionEnabled;
@@ -285,6 +292,18 @@ public class DefaultDeploymentConfiguration extends
}
/**
+ * {@inheritDoc}
+ * <p>
+ * The default path {@link DEFAULT_PUSH_PATH} can be changed by using init
+ * parameter {@link Constants.SERVLET_PARAMETER_PUSH_PATH}.
+ */
+ @Override
+ public String getPushPath() {
+ return getApplicationOrSystemProperty(
+ Constants.SERVLET_PARAMETER_PUSH_PATH, DEFAULT_PUSH_PATH);
+ }
+
+ /**
* Log a warning if Vaadin is not running in production mode.
*/
private void checkProductionMode() {
diff --git a/server/src/com/vaadin/server/DeploymentConfiguration.java b/server/src/com/vaadin/server/DeploymentConfiguration.java
index 968ec7c0c3..06556e28a7 100644
--- a/server/src/com/vaadin/server/DeploymentConfiguration.java
+++ b/server/src/com/vaadin/server/DeploymentConfiguration.java
@@ -195,7 +195,7 @@ public interface DeploymentConfiguration extends Serializable {
*
* @since 7.4
*
- * @return UI class name
+ * @return the name of the widgetset
*/
public String getWidgetset(String defaultValue);
@@ -214,6 +214,14 @@ public interface DeploymentConfiguration extends Serializable {
public String getClassLoaderName();
/**
+ * Returns the push path configuration option value. Should never be null.
+ *
+ * @since 7.4.1
+ * @return the path used with server push
+ */
+ public String getPushPath();
+
+ /**
* Returns to legacy Property.toString() mode used. See
* {@link AbstractProperty#isLegacyToStringEnabled()} for more information.
*
diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java
index 197d9fe416..1f0c7f02b9 100644
--- a/server/src/com/vaadin/server/ServletPortletHelper.java
+++ b/server/src/com/vaadin/server/ServletPortletHelper.java
@@ -124,7 +124,8 @@ public class ServletPortletHelper implements Serializable {
}
public static boolean isPushRequest(VaadinRequest request) {
- return hasPathPrefix(request, ApplicationConstants.PUSH_PATH + '/');
+ return hasPathPrefix(request, request.getService()
+ .getDeploymentConfiguration().getPushPath() + '/');
}
public static void initDefaultUIProvider(VaadinSession session,
diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java
index 36d6910a7a..74f0051e30 100644
--- a/server/src/com/vaadin/server/VaadinService.java
+++ b/server/src/com/vaadin/server/VaadinService.java
@@ -1433,6 +1433,10 @@ public abstract class VaadinService implements Serializable {
ErrorHandler errorHandler = ErrorEvent
.findErrorHandler(vaadinSession);
+ if (errorHandler != null) {
+ errorHandler.error(new ErrorEvent(t));
+ }
+
// if this was an UIDL request, send UIDL back to the client
if (ServletPortletHelper.isUIDLRequest(request)) {
SystemMessages ci = getSystemMessages(
@@ -1454,14 +1458,7 @@ public abstract class VaadinService implements Serializable {
"Failed to write critical notification response to the client",
e);
}
- if (errorHandler != null) {
- errorHandler.error(new ErrorEvent(t));
- }
} else {
- if (errorHandler != null) {
- errorHandler.error(new ErrorEvent(t));
- }
-
// Re-throw other exceptions
throw new ServiceException(t);
}
@@ -1574,20 +1571,11 @@ public abstract class VaadinService implements Serializable {
String message, String details, String url) {
String returnString = "";
try {
- if (message == null) {
- message = details;
- } else if (details != null) {
- message += "<br/><br/>" + details;
- }
-
JsonObject appError = Json.createObject();
- appError.put("caption", caption);
- appError.put("message", message);
- if (url == null) {
- appError.put("url", Json.createNull());
- } else {
- appError.put("url", url);
- }
+ putValueOrJsonNull(appError, "caption", caption);
+ putValueOrJsonNull(appError, "url", url);
+ putValueOrJsonNull(appError, "message",
+ createCriticalNotificationMessage(message, details));
JsonObject meta = Json.createObject();
meta.put("appError", appError);
@@ -1607,6 +1595,26 @@ public abstract class VaadinService implements Serializable {
return "for(;;);[" + returnString + "]";
}
+ private static String createCriticalNotificationMessage(String message,
+ String details) {
+ if (message == null) {
+ return details;
+ } else if (details != null) {
+ return message + "<br/><br/>" + details;
+ }
+
+ return message;
+ }
+
+ private static void putValueOrJsonNull(JsonObject json, String key,
+ String value) {
+ if (value == null) {
+ json.put(key, Json.createNull());
+ } else {
+ json.put(key, value);
+ }
+ }
+
/**
* @deprecated As of 7.0. Will likely change or be removed in a future
* version
diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
index 0819a24ee9..357278f411 100644
--- a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
+++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java
@@ -275,12 +275,10 @@ public class AtmospherePushConnection implements PushConnection {
assert isConnected();
if (resource.isResumed()) {
- // Calling disconnect may end up invoking it again via
- // resource.resume and PushHandler.onResume. Bail out here if
- // the resource is already resumed; this is a bit hacky and should
- // be implemented in a better way in 7.2.
- resource = null;
- state = State.DISCONNECTED;
+ // This can happen for long polling because of
+ // http://dev.vaadin.com/ticket/16919
+ // Once that is fixed, this should never happen
+ connectionLost();
return;
}
@@ -307,8 +305,23 @@ public class AtmospherePushConnection implements PushConnection {
getLogger()
.log(Level.INFO, "Error when closing push connection", e);
}
+ connectionLost();
+ }
+
+ /**
+ * Called when the connection to the client has been lost.
+ *
+ * @since 7.4.1
+ */
+ public void connectionLost() {
resource = null;
- state = State.DISCONNECTED;
+ if (state == State.CONNECTED) {
+ // Guard against connectionLost being (incorrectly) called when
+ // state is PUSH_PENDING or RESPONSE_PENDING
+ // (http://dev.vaadin.com/ticket/16919)
+ state = State.DISCONNECTED;
+ }
+
}
/**
diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java
index 6ee81270cd..22eee70aa0 100644
--- a/server/src/com/vaadin/server/communication/PushHandler.java
+++ b/server/src/com/vaadin/server/communication/PushHandler.java
@@ -28,6 +28,7 @@ import org.atmosphere.cpr.AtmosphereResource;
import org.atmosphere.cpr.AtmosphereResource.TRANSPORT;
import org.atmosphere.cpr.AtmosphereResourceEvent;
import org.atmosphere.cpr.AtmosphereResourceEventListenerAdapter;
+import org.atmosphere.cpr.AtmosphereResourceImpl;
import org.atmosphere.handler.AbstractReflectorAtmosphereHandler;
import com.vaadin.server.ErrorEvent;
@@ -45,6 +46,7 @@ import com.vaadin.server.VaadinSession;
import com.vaadin.shared.ApplicationConstants;
import com.vaadin.shared.communication.PushMode;
import com.vaadin.ui.UI;
+
import elemental.json.JsonException;
/**
@@ -62,7 +64,7 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
throws IOException {
super.onStateChange(event);
if (event.isCancelled() || event.isResumedOnTimeout()) {
- disconnect(event);
+ connectionLost(event);
}
}
@@ -327,17 +329,17 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
public void onDisconnect(AtmosphereResourceEvent event) {
// Log event on trace level
super.onDisconnect(event);
- disconnect(event);
+ connectionLost(event);
}
@Override
public void onThrowable(AtmosphereResourceEvent event) {
getLogger().log(Level.SEVERE, "Exception in push connection",
event.throwable());
- disconnect(event);
+ connectionLost(event);
}
- private void disconnect(AtmosphereResourceEvent event) {
+ private void connectionLost(AtmosphereResourceEvent event) {
// We don't want to use callWithUi here, as it assumes there's a client
// request active and does requestStart and requestEnd among other
// things.
@@ -423,12 +425,8 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
"Connection unexpectedly closed for resource {0} with transport {1}",
new Object[] { id, resource.transport() });
}
- if (pushConnection.isConnected()) {
- // disconnect() assumes the push connection is connected but
- // this method can currently be called more than once during
- // disconnect, depending on the situation
- pushConnection.disconnect();
- }
+
+ pushConnection.connectionLost();
}
} catch (final Exception e) {
@@ -472,6 +470,15 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
*/
private static void sendRefreshAndDisconnect(AtmosphereResource resource)
throws IOException {
+ if (resource instanceof AtmosphereResourceImpl
+ && !((AtmosphereResourceImpl) resource).isInScope()) {
+ // The resource is no longer valid so we should not write
+ // anything to it
+ getLogger()
+ .fine("sendRefreshAndDisconnect called for resource no longer in scope");
+ return;
+ }
+
AtmospherePushConnection connection = new AtmospherePushConnection(null);
connection.connect(resource);
try {
@@ -490,6 +497,14 @@ public class PushHandler extends AtmosphereResourceEventListenerAdapter {
AtmosphereResource resource, String notificationJson) {
// TODO Implemented differently from sendRefreshAndDisconnect
try {
+ if (resource instanceof AtmosphereResourceImpl
+ && !((AtmosphereResourceImpl) resource).isInScope()) {
+ // The resource is no longer valid so we should not write
+ // anything to it
+ getLogger()
+ .fine("sendNotificationAndDisconnect called for resource no longer in scope");
+ return;
+ }
resource.getResponse().getWriter().write(notificationJson);
resource.resume();
} catch (Exception e) {
diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java
index 3a6dc1e55f..02b4e64159 100644
--- a/server/src/com/vaadin/server/communication/UIInitHandler.java
+++ b/server/src/com/vaadin/server/communication/UIInitHandler.java
@@ -198,10 +198,11 @@ public abstract class UIInitHandler extends SynchronizedRequestHandler {
PushMode pushMode = provider.getPushMode(event);
if (pushMode == null) {
- pushMode = session.getService().getDeploymentConfiguration()
- .getPushMode();
+ pushMode = session.getConfiguration().getPushMode();
}
ui.getPushConfiguration().setPushMode(pushMode);
+ ui.getPushConfiguration().setPushPath(
+ session.getConfiguration().getPushPath());
Transport transport = provider.getPushTransport(event);
if (transport != null) {
diff --git a/server/src/com/vaadin/ui/Flash.java b/server/src/com/vaadin/ui/Flash.java
index cd7c00087e..2d0f188b84 100644
--- a/server/src/com/vaadin/ui/Flash.java
+++ b/server/src/com/vaadin/ui/Flash.java
@@ -97,7 +97,7 @@ public class Flash extends AbstractEmbedded {
* Returns the codebase.
*
* @see #setCodebase(String)
- * @since 7.4
+ * @since 7.4.1
* @return Current codebase.
*/
public String getCodebase() {
@@ -126,7 +126,7 @@ public class Flash extends AbstractEmbedded {
* Returns the current codetype.
*
* @see #setCodetype(String)
- * @since 7.4
+ * @since 7.4.1
* @return Current codetype.
*/
public String getCodetype() {
@@ -157,7 +157,7 @@ public class Flash extends AbstractEmbedded {
* Returns current archive.
*
* @see #setArchive(String)
- * @since 7.4
+ * @since 7.4.1
* @return Current archive.
*/
public String getArchive() {
@@ -181,7 +181,7 @@ public class Flash extends AbstractEmbedded {
/**
* Returns standby.
*
- * @since
+ * @since 7.4.1
* @return Standby string.
*/
public String getStandby() {
@@ -247,7 +247,7 @@ public class Flash extends AbstractEmbedded {
*
* @see #setParameter(String, String)
* @see #getParameter(String)
- * @since 7.4
+ * @since 7.4.1
* @return An iterable with declared parameter names.
*/
public Iterable<String> getParameterNames() {
diff --git a/server/src/com/vaadin/ui/Grid.java b/server/src/com/vaadin/ui/Grid.java
index df64ee85ed..77b57bceda 100644
--- a/server/src/com/vaadin/ui/Grid.java
+++ b/server/src/com/vaadin/ui/Grid.java
@@ -2251,7 +2251,7 @@ public class Grid extends AbstractComponent implements SelectionNotifier,
public Column setLastFrozenColumn() {
checkColumnIsAttached();
grid.setFrozenColumnCount(grid.getState(false).columnOrder
- .indexOf(this) + 1);
+ .indexOf(getState().id) + 1);
return this;
}
@@ -5229,4 +5229,17 @@ public class Grid extends AbstractComponent implements SelectionNotifier,
public void removeListener(ItemClickListener listener) {
removeItemClickListener(listener);
}
+
+ /**
+ * Requests that the column widths should be recalculated.
+ * <p>
+ * In most cases Grid will know when column widths need to be recalculated
+ * but this method can be used to force recalculation in situations when
+ * grid does not recalculate automatically.
+ *
+ * @since 7.4.1
+ */
+ public void recalculateColumnWidths() {
+ getRpcProxy(GridClientRpc.class).recalculateColumnWidths();
+ }
}
diff --git a/server/src/com/vaadin/ui/JavaScriptFunction.java b/server/src/com/vaadin/ui/JavaScriptFunction.java
index c006a36d58..071c88350b 100644
--- a/server/src/com/vaadin/ui/JavaScriptFunction.java
+++ b/server/src/com/vaadin/ui/JavaScriptFunction.java
@@ -19,6 +19,7 @@ package com.vaadin.ui;
import java.io.Serializable;
import com.vaadin.server.AbstractJavaScriptExtension;
+
import elemental.json.JsonArray;
/**
@@ -26,9 +27,9 @@ import elemental.json.JsonArray;
* the corresponding JavaScript function is called, the {@link #call(JsonArray)}
* method is invoked.
*
- * @see JavaScript#addFunction(String, JavaScriptCallback)
- * @see AbstractJavaScriptComponent#addFunction(String, JavaScriptCallback)
- * @see AbstractJavaScriptExtension#addFunction(String, JavaScriptCallback)
+ * @see JavaScript#addFunction(String, JavaScriptFunction)
+ * @see AbstractJavaScriptComponent#addFunction(String, JavaScriptFunction)
+ * @see AbstractJavaScriptExtension#addFunction(String, JavaScriptFunction)
*
* @author Vaadin Ltd
* @since 7.0.0
diff --git a/server/src/com/vaadin/ui/PushConfiguration.java b/server/src/com/vaadin/ui/PushConfiguration.java
index 90ad28542c..d5e89b4b14 100644
--- a/server/src/com/vaadin/ui/PushConfiguration.java
+++ b/server/src/com/vaadin/ui/PushConfiguration.java
@@ -105,6 +105,26 @@ public interface PushConfiguration extends Serializable {
public void setFallbackTransport(Transport fallbackTransport);
/**
+ * Sets the path that is used with push.
+ *
+ * @since 7.4.1
+ * @param pushPath
+ * The path to be used with push
+ *
+ * @throws IllegalArgumentException
+ * if the argument is null or empty.
+ */
+ public void setPushPath(String pushPath);
+
+ /**
+ * Returns the path used with push.
+ *
+ * @since 7.4.1
+ * @return The path that is used with push
+ */
+ public String getPushPath();
+
+ /**
* Returns the given parameter, if set.
* <p>
* This method provides low level access to push parameters and is typically
@@ -258,6 +278,32 @@ class PushConfigurationImpl implements PushConfiguration {
/*
* (non-Javadoc)
*
+ * @see com.vaadin.ui.PushConfiguration#setPushPath(java.lang.String)
+ */
+ @Override
+ public void setPushPath(String pushPath) {
+ if (pushPath != null && !pushPath.isEmpty()) {
+ getState().pushPath = pushPath;
+ } else {
+ throw new IllegalArgumentException(
+ "Push path can't be empty or null");
+ }
+
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see com.vaadin.ui.PushConfiguration#getPushPath()
+ */
+ @Override
+ public String getPushPath() {
+ return getState(false).pushPath;
+ }
+
+ /*
+ * (non-Javadoc)
+ *
* @see com.vaadin.ui.PushConfiguration#getParameter(java.lang.String)
*/
@Override
@@ -290,5 +336,4 @@ class PushConfigurationImpl implements PushConfiguration {
return Collections.unmodifiableCollection(getState(false).parameters
.keySet());
}
-
}
diff --git a/server/src/com/vaadin/ui/Slider.java b/server/src/com/vaadin/ui/Slider.java
index 66ed1a48f4..dad4d295bf 100644
--- a/server/src/com/vaadin/ui/Slider.java
+++ b/server/src/com/vaadin/ui/Slider.java
@@ -161,6 +161,11 @@ public class Slider extends AbstractField<Double> {
*/
public void setMax(double max) {
getState().maxValue = max;
+
+ if (getMin() > max) {
+ getState().minValue = max;
+ }
+
if (getValue() > max) {
setValue(max);
}
@@ -179,11 +184,16 @@ public class Slider extends AbstractField<Double> {
* Set the minimum slider value. If the current value of the slider is
* smaller than this, the value is set to the new minimum.
*
- * @param max
+ * @param min
* The new minimum slider value
*/
public void setMin(double min) {
getState().minValue = min;
+
+ if (getMax() < min) {
+ getState().maxValue = min;
+ }
+
if (getValue() < min) {
setValue(min);
}
@@ -260,12 +270,12 @@ public class Slider extends AbstractField<Double> {
newValue = (int) (v * Math.pow(10, resolution));
newValue = newValue / Math.pow(10, resolution);
if (getMin() > newValue || getMax() < newValue) {
- throw new ValueOutOfBoundsException(value);
+ throw new ValueOutOfBoundsException(newValue);
}
} else {
newValue = (int) v;
if (getMin() > newValue || getMax() < newValue) {
- throw new ValueOutOfBoundsException(value);
+ throw new ValueOutOfBoundsException(newValue);
}
}
@@ -313,6 +323,8 @@ public class Slider extends AbstractField<Double> {
* @param valueOutOfBounds
*/
public ValueOutOfBoundsException(Double valueOutOfBounds) {
+ super(String.format("Value %s is out of bounds: [%s, %s]",
+ valueOutOfBounds, getMin(), getMax()));
value = valueOutOfBounds;
}
diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java
index 66f893e04a..8dd600ddd0 100644
--- a/server/src/com/vaadin/ui/UI.java
+++ b/server/src/com/vaadin/ui/UI.java
@@ -425,7 +425,12 @@ public abstract class UI extends AbstractSingleComponentContainer implements
+ ".");
} else {
if (session == null) {
- detach();
+ try {
+ detach();
+ } catch (Exception e) {
+ getLogger().log(Level.WARNING,
+ "Error while detaching UI from session", e);
+ }
// Disable push when the UI is detached. Otherwise the
// push connection and possibly VaadinSession will live on.
getPushConfiguration().setPushMode(PushMode.DISABLED);
diff --git a/server/src/com/vaadin/ui/declarative/Design.java b/server/src/com/vaadin/ui/declarative/Design.java
index dc96e789bf..1b8585e6f6 100644
--- a/server/src/com/vaadin/ui/declarative/Design.java
+++ b/server/src/com/vaadin/ui/declarative/Design.java
@@ -57,6 +57,119 @@ import com.vaadin.ui.declarative.DesignContext.ComponentCreationListener;
* @author Vaadin Ltd
*/
public class Design implements Serializable {
+
+ /**
+ * Callback for creating instances of a given component class when reading
+ * designs. The default implementation, {@link DefaultComponentFactory} will
+ * use <code>Class.forName(className).newInstance()</code>, which might not
+ * be suitable e.g. in an OSGi environment or if the Component instances
+ * should be created as managed CDI beans.
+ * <p>
+ * Use {@link Design#setComponentFactory(ComponentFactory)} to configure
+ * Vaadin to use a custom component factory.
+ *
+ *
+ * @since 7.4.1
+ */
+ public interface ComponentFactory extends Serializable {
+ /**
+ * Creates a component based on the fully qualified name derived from
+ * the tag name in the design.
+ *
+ * @param fullyQualifiedClassName
+ * the fully qualified name of the component to create
+ * @param context
+ * the design context for which the component is created
+ *
+ * @return a newly created component
+ */
+ public Component createComponent(String fullyQualifiedClassName,
+ DesignContext context);
+ }
+
+ /**
+ * Default implementation of {@link ComponentFactory}, using
+ * <code>Class.forName(className).newInstance()</code> for finding the
+ * component class and creating a component instance.
+ *
+ * @since 7.4.1
+ */
+ public static class DefaultComponentFactory implements ComponentFactory {
+ @Override
+ public Component createComponent(String fullyQualifiedClassName,
+ DesignContext context) {
+ Class<? extends Component> componentClass = resolveComponentClass(
+ fullyQualifiedClassName, context);
+
+ assert Component.class.isAssignableFrom(componentClass) : "resolveComponentClass returned "
+ + componentClass + " which is not a Vaadin Component class";
+
+ try {
+ return componentClass.newInstance();
+ } catch (Exception e) {
+ throw new DesignException("Could not create component "
+ + fullyQualifiedClassName, e);
+ }
+ }
+
+ /**
+ * Resolves a component class based on the fully qualified name of the
+ * class.
+ *
+ * @param qualifiedClassName
+ * the fully qualified name of the resolved class
+ * @param context
+ * the design context for which the class is resolved
+ * @return a component class object representing the provided class name
+ */
+ protected Class<? extends Component> resolveComponentClass(
+ String qualifiedClassName, DesignContext context) {
+ try {
+ Class<?> componentClass = Class.forName(qualifiedClassName);
+ return componentClass.asSubclass(Component.class);
+ } catch (ClassNotFoundException e) {
+ throw new DesignException(
+ "Unable to load component for design", e);
+ }
+ }
+
+ }
+
+ private static volatile ComponentFactory componentFactory = new DefaultComponentFactory();
+
+ /**
+ * Sets the component factory that is used for creating component instances
+ * based on fully qualified class names derived from a design file.
+ * <p>
+ * Please note that this setting is global, so care should be taken to avoid
+ * conflicting changes.
+ *
+ * @param componentFactory
+ * the component factory to set; not <code>null</code>
+ *
+ * @since 7.4.1
+ */
+ public static void setComponentFactory(ComponentFactory componentFactory) {
+ if (componentFactory == null) {
+ throw new IllegalArgumentException(
+ "Cannot set null component factory");
+ }
+ Design.componentFactory = componentFactory;
+ }
+
+ /**
+ * Gets the currently used component factory.
+ *
+ * @see #setComponentFactory(ComponentFactory)
+ *
+ * @return the component factory
+ *
+ * @since 7.4.1
+ */
+ public static ComponentFactory getComponentFactory() {
+ return componentFactory;
+ }
+
/**
* Parses the given input stream into a jsoup document
*
diff --git a/server/src/com/vaadin/ui/declarative/DesignContext.java b/server/src/com/vaadin/ui/declarative/DesignContext.java
index 5f160d6f26..09fefd0a6b 100644
--- a/server/src/com/vaadin/ui/declarative/DesignContext.java
+++ b/server/src/com/vaadin/ui/declarative/DesignContext.java
@@ -31,6 +31,7 @@ import com.vaadin.annotations.DesignRoot;
import com.vaadin.shared.util.SharedUtil;
import com.vaadin.ui.Component;
import com.vaadin.ui.HasComponents;
+import com.vaadin.ui.declarative.Design.ComponentFactory;
/**
* This class contains contextual information that is collected when a component
@@ -482,14 +483,17 @@ public class DesignContext implements Serializable {
private Component instantiateComponent(Node node) {
// Extract the package and class names.
String qualifiedClassName = tagNameToClassName(node);
- try {
- Class<? extends Component> componentClass = resolveComponentClass(qualifiedClassName);
- Component newComponent = componentClass.newInstance();
- return newComponent;
- } catch (Exception e) {
- throw new DesignException("No component class could be found for "
- + node.nodeName() + ".", e);
+
+ ComponentFactory factory = Design.getComponentFactory();
+ Component component = factory.createComponent(qualifiedClassName, this);
+
+ if (component == null) {
+ throw new DesignException("Got unexpected null component from "
+ + factory.getClass().getName() + " for class "
+ + qualifiedClassName);
}
+
+ return component;
}
/**
@@ -530,39 +534,6 @@ public class DesignContext implements Serializable {
return packageName + "." + className;
}
- @SuppressWarnings("unchecked")
- private Class<? extends Component> resolveComponentClass(
- String qualifiedClassName) throws ClassNotFoundException {
- Class<?> componentClass = null;
- componentClass = Class.forName(qualifiedClassName);
-
- // Check that we're dealing with a Component.
- if (isComponent(componentClass)) {
- return (Class<? extends Component>) componentClass;
- } else {
- throw new IllegalArgumentException(String.format(
- "Resolved class %s is not a %s.", componentClass.getName(),
- Component.class.getName()));
- }
- }
-
- /**
- * Returns {@code true} if the given {@link Class} implements the
- * {@link Component} interface of Vaadin Framework otherwise {@code false}.
- *
- * @param componentClass
- * {@link Class} to check against {@link Component} interface.
- * @return {@code true} if the given {@link Class} is a {@link Component},
- * {@code false} otherwise.
- */
- private static boolean isComponent(Class<?> componentClass) {
- if (componentClass != null) {
- return Component.class.isAssignableFrom(componentClass);
- } else {
- return false;
- }
- }
-
/**
* Returns the root component of a created component hierarchy.
*