diff options
50 files changed, 1119 insertions, 503 deletions
diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java index a54798e5d6..6d322c734e 100644 --- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/RpcProxyGenerator.java @@ -29,6 +29,7 @@ import com.google.gwt.core.ext.typeinfo.JParameter; import com.google.gwt.core.ext.typeinfo.TypeOracle; import com.google.gwt.user.rebind.ClassSourceFileComposerFactory; import com.google.gwt.user.rebind.SourceWriter; +import com.vaadin.shared.annotations.Delayed; import com.vaadin.shared.communication.MethodInvocation; import com.vaadin.shared.communication.ServerRpc; import com.vaadin.terminal.gwt.client.ApplicationConnection; @@ -129,6 +130,10 @@ public class RpcProxyGenerator extends Generator { writer.println(" {"); writer.indent(); + Delayed delayedAnnotation = m.getAnnotation(Delayed.class); + boolean delayed = delayedAnnotation != null; + boolean lastonly = delayed && delayedAnnotation.lastonly(); + writer.print("this.connector.getConnection().addMethodInvocationToQueue(new MethodInvocation(this.connector.getConnectorId(), \"" + requestedType.getQualifiedBinaryName() + "\", \""); writer.print(m.getName()); @@ -145,7 +150,7 @@ public class RpcProxyGenerator extends Generator { writer.print(p.getName()); } - writer.println("}), true);"); + writer.println("}), " + delayed + ", " + lastonly + ");"); writer.outdent(); writer.println("}"); diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java index 2fc9645940..cc92551846 100644 --- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/SerializerGenerator.java @@ -32,6 +32,7 @@ import com.google.gwt.core.ext.typeinfo.JClassType; import com.google.gwt.core.ext.typeinfo.JEnumConstant; import com.google.gwt.core.ext.typeinfo.JEnumType; import com.google.gwt.core.ext.typeinfo.JMethod; +import com.google.gwt.core.ext.typeinfo.JPackage; import com.google.gwt.core.ext.typeinfo.JPrimitiveType; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.core.ext.typeinfo.TypeOracleException; @@ -59,8 +60,6 @@ import com.vaadin.terminal.gwt.client.communication.SerializerMap; public class SerializerGenerator extends Generator { private static final String SUBTYPE_SEPARATOR = "___"; - private static String serializerPackageName = SerializerMap.class - .getPackage().getName(); @Override public String generate(TreeLogger logger, GeneratorContext context, @@ -75,8 +74,8 @@ public class SerializerGenerator extends Generator { String serializerClassName = getSerializerSimpleClassName(type); try { // Generate class source code - generateClass(logger, context, type, serializerPackageName, - serializerClassName); + generateClass(logger, context, type, + getSerializerPackageName(type), serializerClassName); } catch (Exception e) { logger.log(TreeLogger.ERROR, "SerializerGenerator failed for " + type.getQualifiedSourceName(), e); @@ -465,6 +464,21 @@ public class SerializerGenerator extends Generator { } public static String getFullyQualifiedSerializerClassName(JClassType type) { - return serializerPackageName + "." + getSerializerSimpleClassName(type); + return getSerializerPackageName(type) + "." + + getSerializerSimpleClassName(type); + } + + private static String getSerializerPackageName(JClassType type) { + JPackage typePackage = type.getPackage(); + if (typePackage == null) { + return SerializerMap.class.getPackage().getName(); + } else { + String packageName = typePackage.getName(); + // Dev mode classloader gets unhappy for some java packages + if (packageName.startsWith("java.")) { + packageName = "com.vaadin." + packageName; + } + return packageName; + } } } diff --git a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java index dae6f2821e..1f5b301802 100644 --- a/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java +++ b/client-compiler/src/com/vaadin/terminal/gwt/widgetsetutils/WidgetMapGenerator.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; +import java.util.Map; import java.util.TreeSet; import com.google.gwt.core.ext.Generator; @@ -116,8 +117,10 @@ public class WidgetMapGenerator extends Generator { * Logger object * @param context * Generator context + * @throws UnableToCompleteException */ - private void generateClass(TreeLogger logger, GeneratorContext context) { + private void generateClass(TreeLogger logger, GeneratorContext context) + throws UnableToCompleteException { // get print writer that receives the source code PrintWriter printWriter = null; printWriter = context.tryCreate(logger, packageName, className); @@ -147,7 +150,7 @@ public class WidgetMapGenerator extends Generator { logConnectors(logger, context, connectors); // generator constructor source code - generateImplementationDetector(sourceWriter, connectors); + generateImplementationDetector(logger, sourceWriter, connectors); generateInstantiatorMethod(sourceWriter, connectors); // close generated class sourceWriter.outdent(); @@ -369,13 +372,18 @@ public class WidgetMapGenerator extends Generator { /** * + * @param logger + * logger to print messages to * @param sourceWriter * Source writer to output source code * @param paintablesHavingWidgetAnnotation + * @throws UnableToCompleteException */ private void generateImplementationDetector( + TreeLogger logger, SourceWriter sourceWriter, - Collection<Class<? extends ServerConnector>> paintablesHavingWidgetAnnotation) { + Collection<Class<? extends ServerConnector>> paintablesHavingWidgetAnnotation) + throws UnableToCompleteException { sourceWriter .println("public Class<? extends " + serverConnectorClassName @@ -385,8 +393,24 @@ public class WidgetMapGenerator extends Generator { sourceWriter .println("fullyQualifiedName = fullyQualifiedName.intern();"); + // Keep track of encountered mappings to detect conflicts + Map<Class<? extends ClientConnector>, Class<? extends ServerConnector>> mappings = new HashMap<Class<? extends ClientConnector>, Class<? extends ServerConnector>>(); + for (Class<? extends ServerConnector> connectorClass : paintablesHavingWidgetAnnotation) { Class<? extends ClientConnector> clientConnectorClass = getClientConnectorClass(connectorClass); + + // Check for conflicts + Class<? extends ServerConnector> prevousMapping = mappings.put( + clientConnectorClass, connectorClass); + if (prevousMapping != null) { + logger.log(Type.ERROR, + "Both " + connectorClass.getName() + " and " + + prevousMapping.getName() + + " have @Connect referring to " + + clientConnectorClass.getName() + "."); + throw new UnableToCompleteException(); + } + sourceWriter.print("if ( fullyQualifiedName == \""); sourceWriter.print(clientConnectorClass.getName()); sourceWriter.print("\" ) { ensureInstantiator(" diff --git a/client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java b/client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java index a8852fe9fa..32b15d6d87 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java +++ b/client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java @@ -22,6 +22,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -54,9 +55,9 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.ComponentState; import com.vaadin.shared.Version; +import com.vaadin.shared.communication.LegacyChangeVariablesInvocation; import com.vaadin.shared.communication.MethodInvocation; import com.vaadin.shared.communication.SharedState; -import com.vaadin.shared.communication.UidlValue; import com.vaadin.terminal.gwt.client.ApplicationConfiguration.ErrorMessage; import com.vaadin.terminal.gwt.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.terminal.gwt.client.ResourceLoader.ResourceLoadListener; @@ -132,7 +133,18 @@ public class ApplicationConnection { private final HashMap<String, String> resourcesMap = new HashMap<String, String>(); - private ArrayList<MethodInvocation> pendingInvocations = new ArrayList<MethodInvocation>(); + /** + * The pending method invocations that will be send to the server by + * {@link #sendPendingCommand}. The key is defined differently based on + * whether the method invocation is enqueued with lastonly. With lastonly + * enabled, the method signature ( {@link MethodInvocation#getLastonlyTag()} + * ) is used as the key to make enable removing a previously enqueued + * invocation. Without lastonly, an incremental id based on + * {@link #lastInvocationTag} is used to get unique values. + */ + private LinkedHashMap<String, MethodInvocation> pendingInvocations = new LinkedHashMap<String, MethodInvocation>(); + + private int lastInvocationTag = 0; private WidgetSet widgetSet; @@ -155,7 +167,7 @@ public class ApplicationConnection { private ApplicationConfiguration configuration; /** List of pending variable change bursts that must be submitted in order */ - private final ArrayList<ArrayList<MethodInvocation>> pendingBursts = new ArrayList<ArrayList<MethodInvocation>>(); + private final ArrayList<LinkedHashMap<String, MethodInvocation>> pendingBursts = new ArrayList<LinkedHashMap<String, MethodInvocation>>(); /** Timer for automatic refirect to SessionExpiredURL */ private Timer redirectTimer; @@ -886,12 +898,11 @@ public class ApplicationConnection { private void checkForPendingVariableBursts() { cleanVariableBurst(pendingInvocations); if (pendingBursts.size() > 0) { - for (Iterator<ArrayList<MethodInvocation>> iterator = pendingBursts - .iterator(); iterator.hasNext();) { - cleanVariableBurst(iterator.next()); + for (LinkedHashMap<String, MethodInvocation> pendingBurst : pendingBursts) { + cleanVariableBurst(pendingBurst); } - ArrayList<MethodInvocation> nextBurst = pendingBursts.get(0); - pendingBursts.remove(0); + LinkedHashMap<String, MethodInvocation> nextBurst = pendingBursts + .remove(0); buildAndSendVariableBurst(nextBurst, false); } } @@ -902,13 +913,15 @@ public class ApplicationConnection { * * @param variableBurst */ - private void cleanVariableBurst(ArrayList<MethodInvocation> variableBurst) { - for (int i = 1; i < variableBurst.size(); i++) { - String id = variableBurst.get(i).getConnectorId(); + private void cleanVariableBurst( + LinkedHashMap<String, MethodInvocation> variableBurst) { + Iterator<MethodInvocation> iterator = variableBurst.values().iterator(); + while (iterator.hasNext()) { + String id = iterator.next().getConnectorId(); if (!getConnectorMap().hasConnector(id) && !getConnectorMap().isDragAndDropPaintable(id)) { // variable owner does not exist anymore - variableBurst.remove(i); + iterator.remove(); VConsole.log("Removed variable from removed component: " + id); } } @@ -1460,9 +1473,12 @@ public class ApplicationConnection { .getName(), null), stateJson, state, ApplicationConnection.this); - StateChangeEvent event = GWT - .create(StateChangeEvent.class); - event.setConnector(connector); + Set<String> changedProperties = new HashSet<String>(); + addJsonFields(stateJson, changedProperties, ""); + + StateChangeEvent event = new StateChangeEvent( + connector, changedProperties); + events.add(event); } } catch (final Throwable e) { @@ -1474,6 +1490,30 @@ public class ApplicationConnection { } /** + * Recursively adds the names of all fields in all objects in the + * provided json object. + * + * @param json + * the json object to process + * @param fields + * a set of all currently added fields + * @param context + * the base name of the current object + */ + private void addJsonFields(JSONObject json, Set<String> fields, + String context) { + for (String key : json.keySet()) { + String fieldName = context + key; + fields.add(fieldName); + + JSONObject object = json.get(key).isObject(); + if (object != null) { + addJsonFields(object, fields, fieldName + "."); + } + } + } + + /** * Updates the connector hierarchy and returns a list of events that * should be fired after update of the hierarchy and the state is * done. @@ -1693,12 +1733,10 @@ public class ApplicationConnection { private void addVariableToQueue(String connectorId, String variableName, Object value, boolean immediate) { + boolean lastOnly = !immediate; // note that type is now deduced from value - // TODO could eliminate invocations of same shared variable setter - addMethodInvocationToQueue(new MethodInvocation(connectorId, - ApplicationConstants.UPDATE_VARIABLE_INTERFACE, - ApplicationConstants.UPDATE_VARIABLE_METHOD, new Object[] { - variableName, new UidlValue(value) }), immediate); + addMethodInvocationToQueue(new LegacyChangeVariablesInvocation( + connectorId, variableName, value), lastOnly, lastOnly); } /** @@ -1708,16 +1746,31 @@ public class ApplicationConnection { * * @param invocation * RPC method invocation - * @param immediate - * true to trigger sending within a short time window (possibly - * combining subsequent calls to a single request), false to let - * the framework delay sending of RPC calls and variable changes - * until the next immediate change + * @param delayed + * <code>false</code> to trigger sending within a short time + * window (possibly combining subsequent calls to a single + * request), <code>true</code> to let the framework delay sending + * of RPC calls and variable changes until the next non-delayed + * change + * @param lastonly + * <code>true</code> to remove all previously delayed invocations + * of the same method that were also enqueued with lastonly set + * to <code>true</code>. <code>false</code> to add invocation to + * the end of the queue without touching previously enqueued + * invocations. */ public void addMethodInvocationToQueue(MethodInvocation invocation, - boolean immediate) { - pendingInvocations.add(invocation); - if (immediate) { + boolean delayed, boolean lastonly) { + String tag; + if (lastonly) { + tag = invocation.getLastonlyTag(); + assert !tag.matches("\\d+") : "getLastonlyTag value must have at least one non-digit character"; + pendingInvocations.remove(tag); + } else { + tag = Integer.toString(lastInvocationTag++); + } + pendingInvocations.put(tag, invocation); + if (!delayed) { sendPendingVariableChanges(); } } @@ -1748,14 +1801,15 @@ public class ApplicationConnection { }; private boolean deferedSendPending = false; - @SuppressWarnings("unchecked") private void doSendPendingVariableChanges() { if (applicationRunning) { if (hasActiveRequest()) { // skip empty queues if there are pending bursts to be sent if (pendingInvocations.size() > 0 || pendingBursts.size() == 0) { pendingBursts.add(pendingInvocations); - pendingInvocations = new ArrayList<MethodInvocation>(); + pendingInvocations = new LinkedHashMap<String, MethodInvocation>(); + // Keep tag string short + lastInvocationTag = 0; } } else { buildAndSendVariableBurst(pendingInvocations, false); @@ -1776,17 +1830,18 @@ public class ApplicationConnection { * Should we use synchronous request? */ private void buildAndSendVariableBurst( - ArrayList<MethodInvocation> pendingInvocations, boolean forceSync) { + LinkedHashMap<String, MethodInvocation> pendingInvocations, + boolean forceSync) { final StringBuffer req = new StringBuffer(); while (!pendingInvocations.isEmpty()) { if (ApplicationConfiguration.isDebugMode()) { - Util.logVariableBurst(this, pendingInvocations); + Util.logVariableBurst(this, pendingInvocations.values()); } JSONArray reqJson = new JSONArray(); - for (MethodInvocation invocation : pendingInvocations) { + for (MethodInvocation invocation : pendingInvocations.values()) { JSONArray invocationJson = new JSONArray(); invocationJson.set(0, new JSONString(invocation.getConnectorId())); @@ -1810,6 +1865,8 @@ public class ApplicationConnection { req.append(escapeBurstContents(reqJson.toString())); pendingInvocations.clear(); + // Keep tag string short + lastInvocationTag = 0; // Append all the bursts to this synchronous request if (forceSync && !pendingBursts.isEmpty()) { pendingInvocations = pendingBursts.get(0); diff --git a/client/src/com/vaadin/terminal/gwt/client/JavaScriptConnectorHelper.java b/client/src/com/vaadin/terminal/gwt/client/JavaScriptConnectorHelper.java index 98c014b5ec..6494ae3480 100644 --- a/client/src/com/vaadin/terminal/gwt/client/JavaScriptConnectorHelper.java +++ b/client/src/com/vaadin/terminal/gwt/client/JavaScriptConnectorHelper.java @@ -267,7 +267,7 @@ public class JavaScriptConnectorHelper { } connector.getConnection().addMethodInvocationToQueue( new MethodInvocation(connector.getConnectorId(), iface, method, - parameters), true); + parameters), false, false); } private String findWildcardInterface(String method) { @@ -298,7 +298,8 @@ public class JavaScriptConnectorHelper { connector.getConnectorId(), "com.vaadin.ui.JavaScript$JavaScriptCallbackRpc", "call", new Object[] { name, new JSONArray(arguments) }); - connector.getConnection().addMethodInvocationToQueue(invocation, true); + connector.getConnection().addMethodInvocationToQueue(invocation, false, + false); } public void setNativeState(JavaScriptObject state) { diff --git a/client/src/com/vaadin/terminal/gwt/client/ServerConnector.java b/client/src/com/vaadin/terminal/gwt/client/ServerConnector.java index fa78f886a7..8788de74bf 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ServerConnector.java +++ b/client/src/com/vaadin/terminal/gwt/client/ServerConnector.java @@ -75,7 +75,7 @@ public interface ServerConnector extends Connector { String rpcInterfaceId); /** - * Adds a handler that is called whenever some part of the state has been + * Adds a handler that is called whenever any part of the state has been * updated by the server. * * @param handler @@ -86,13 +86,19 @@ public interface ServerConnector extends Connector { public HandlerRegistration addStateChangeHandler(StateChangeHandler handler); /** - * Removes the specified StateChangeHandler from this connector. The handler - * will no longer be notified of the state is updated by the server. + * Adds a handler that is called whenever the given part of the state has + * been updated by the server. * + * @param propertyName + * the name of the property for which the handler should be + * called * @param handler - * The handler that should be removed. + * The handler that should be added. + * @return A handler registration reference that can be used to unregister + * the handler */ - public void removeStateChangeHandler(StateChangeHandler handler); + public HandlerRegistration addStateChangeHandler(String propertyName, + StateChangeHandler handler); /** * Sends the given event to all registered handlers. diff --git a/client/src/com/vaadin/terminal/gwt/client/Util.java b/client/src/com/vaadin/terminal/gwt/client/Util.java index 571258dbe3..96344f0792 100644 --- a/client/src/com/vaadin/terminal/gwt/client/Util.java +++ b/client/src/com/vaadin/terminal/gwt/client/Util.java @@ -874,13 +874,13 @@ public class Util { } static void logVariableBurst(ApplicationConnection c, - ArrayList<MethodInvocation> loggedBurst) { + Collection<MethodInvocation> loggedBurst) { try { VConsole.log("Variable burst to be sent to server:"); String curId = null; ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>(); - for (int i = 0; i < loggedBurst.size(); i++) { - String id = loggedBurst.get(i).getConnectorId(); + for (MethodInvocation methodInvocation : loggedBurst) { + String id = methodInvocation.getConnectorId(); if (curId == null) { curId = id; @@ -889,7 +889,7 @@ public class Util { invocations.clear(); curId = id; } - invocations.add(loggedBurst.get(i)); + invocations.add(methodInvocation); } if (!invocations.isEmpty()) { printConnectorInvocations(invocations, curId, c); diff --git a/client/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java b/client/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java index e1847bdab7..8ed32bc94b 100644 --- a/client/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java +++ b/client/src/com/vaadin/terminal/gwt/client/communication/StateChangeEvent.java @@ -16,8 +16,11 @@ package com.vaadin.terminal.gwt.client.communication; import java.io.Serializable; +import java.util.Collections; +import java.util.Set; import com.google.gwt.event.shared.EventHandler; +import com.vaadin.terminal.gwt.client.ServerConnector; import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler; public class StateChangeEvent extends @@ -27,12 +30,25 @@ public class StateChangeEvent extends */ public static final Type<StateChangeHandler> TYPE = new Type<StateChangeHandler>(); + private Set<String> changedProperties; + @Override public Type<StateChangeHandler> getAssociatedType() { return TYPE; } - public StateChangeEvent() { + /** + * Creates a new state change event. + * + * @param connector + * the event whose state has changed + * @param changedProperties + * a set of names of the changed properties + */ + public StateChangeEvent(ServerConnector connector, + Set<String> changedProperties) { + setConnector(connector); + this.changedProperties = changedProperties; } @Override @@ -40,7 +56,30 @@ public class StateChangeEvent extends listener.onStateChanged(this); } + /** + * Event handler that gets notified whenever any part of the state has been + * updated by the server. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ public interface StateChangeHandler extends Serializable, EventHandler { + /** + * Notifies the event handler that the state has changed. + * + * @param stateChangeEvent + * the state change event with details about the change + */ public void onStateChanged(StateChangeEvent stateChangeEvent); } + + /** + * Gets the properties that have changed. + * + * @return a set of names of the changed properties + */ + public Set<String> getChangedProperties() { + return Collections.unmodifiableSet(changedProperties); + } } diff --git a/client/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerConnector.java b/client/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerConnector.java index 5cc5911bb1..46578b0641 100644 --- a/client/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerConnector.java +++ b/client/src/com/vaadin/terminal/gwt/client/extensions/javascriptmanager/JavaScriptManagerConnector.java @@ -124,7 +124,7 @@ public class JavaScriptManagerConnector extends AbstractExtensionConnector { getConnection().addMethodInvocationToQueue( new MethodInvocation(getConnectorId(), "com.vaadin.ui.JavaScript$JavaScriptCallbackRpc", - "call", parameters), true); + "call", parameters), false, false); } @Override diff --git a/client/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java b/client/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java index 09b7556293..435fff8a5b 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java +++ b/client/src/com/vaadin/terminal/gwt/client/ui/AbstractConnector.java @@ -48,6 +48,7 @@ public abstract class AbstractConnector implements ServerConnector, private String id; private HandlerManager handlerManager; + private Map<String, HandlerManager> statePropertyHandlerManagers; private Map<String, Collection<ClientRpc>> rpcImplementations; private final boolean debugLogging = false; @@ -168,6 +169,17 @@ public abstract class AbstractConnector implements ServerConnector, if (handlerManager != null) { handlerManager.fireEvent(event); } + if (statePropertyHandlerManagers != null + && event instanceof StateChangeEvent) { + for (String property : ((StateChangeEvent) event) + .getChangedProperties()) { + HandlerManager manager = statePropertyHandlerManagers + .get(property); + if (manager != null) { + manager.fireEvent(event); + } + } + } } protected HandlerManager ensureHandlerManager() { @@ -185,10 +197,25 @@ public abstract class AbstractConnector implements ServerConnector, } @Override - public void removeStateChangeHandler(StateChangeHandler handler) { - ensureHandlerManager().removeHandler(StateChangeEvent.TYPE, handler); + public HandlerRegistration addStateChangeHandler(String propertyName, + StateChangeHandler handler) { + return ensureHandlerManager(propertyName).addHandler( + StateChangeEvent.TYPE, handler); + } + + private HandlerManager ensureHandlerManager(String propertyName) { + if (statePropertyHandlerManagers == null) { + statePropertyHandlerManagers = new HashMap<String, HandlerManager>(); + } + HandlerManager manager = statePropertyHandlerManagers.get(propertyName); + if (manager == null) { + manager = new HandlerManager(this); + statePropertyHandlerManagers.put(propertyName, manager); + } + return manager; } + @Override public void onStateChanged(StateChangeEvent stateChangeEvent) { if (debugLogging) { VConsole.log("State change event for " diff --git a/client/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java index 59e187014c..59f90a9840 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java +++ b/client/src/com/vaadin/terminal/gwt/client/ui/button/ButtonConnector.java @@ -16,6 +16,8 @@ package com.vaadin.terminal.gwt.client.ui.button; +import java.util.Set; + import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ClickEvent; @@ -34,6 +36,7 @@ import com.vaadin.terminal.gwt.client.EventHelper; import com.vaadin.terminal.gwt.client.MouseEventDetailsBuilder; import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; +import com.vaadin.terminal.gwt.client.communication.StateChangeEvent.StateChangeHandler; import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector; import com.vaadin.terminal.gwt.client.ui.Icon; import com.vaadin.ui.Button; @@ -59,6 +62,47 @@ public class ButtonConnector extends AbstractComponentConnector implements super.init(); getWidget().addClickHandler(this); getWidget().client = getConnection(); + addStateChangeHandler("errorMessage", new StateChangeHandler() { + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + if (null != getState().getErrorMessage()) { + if (getWidget().errorIndicatorElement == null) { + getWidget().errorIndicatorElement = DOM.createSpan(); + getWidget().errorIndicatorElement + .setClassName("v-errorindicator"); + } + getWidget().wrapper.insertBefore( + getWidget().errorIndicatorElement, + getWidget().captionElement); + + } else if (getWidget().errorIndicatorElement != null) { + getWidget().wrapper + .removeChild(getWidget().errorIndicatorElement); + getWidget().errorIndicatorElement = null; + } + } + }); + + addStateChangeHandler("icon", new StateChangeHandler() { + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + if (getState().getIcon() != null) { + if (getWidget().icon == null) { + getWidget().icon = new Icon(getConnection()); + getWidget().wrapper.insertBefore( + getWidget().icon.getElement(), + getWidget().captionElement); + } + getWidget().icon.setUri(getState().getIcon().getURL()); + } else { + if (getWidget().icon != null) { + getWidget().wrapper.removeChild(getWidget().icon + .getElement()); + getWidget().icon = null; + } + } + } + }); } @Override @@ -68,39 +112,15 @@ public class ButtonConnector extends AbstractComponentConnector implements focusHandlerRegistration); blurHandlerRegistration = EventHelper.updateBlurHandler(this, blurHandlerRegistration); - // Set text - if (getState().isHtmlContentAllowed()) { - getWidget().setHtml(getState().getCaption()); - } else { - getWidget().setText(getState().getCaption()); - } - // handle error - if (null != getState().getErrorMessage()) { - if (getWidget().errorIndicatorElement == null) { - getWidget().errorIndicatorElement = DOM.createSpan(); - getWidget().errorIndicatorElement - .setClassName("v-errorindicator"); - } - getWidget().wrapper.insertBefore(getWidget().errorIndicatorElement, - getWidget().captionElement); - - } else if (getWidget().errorIndicatorElement != null) { - getWidget().wrapper.removeChild(getWidget().errorIndicatorElement); - getWidget().errorIndicatorElement = null; - } - - if (getState().getIcon() != null) { - if (getWidget().icon == null) { - getWidget().icon = new Icon(getConnection()); - getWidget().wrapper.insertBefore(getWidget().icon.getElement(), - getWidget().captionElement); - } - getWidget().icon.setUri(getState().getIcon().getURL()); - } else { - if (getWidget().icon != null) { - getWidget().wrapper.removeChild(getWidget().icon.getElement()); - getWidget().icon = null; + Set<String> changedProperties = stateChangeEvent.getChangedProperties(); + if (changedProperties.contains("caption") + || changedProperties.contains("htmlContentAllowed")) { + // Set text + if (getState().isHtmlContentAllowed()) { + getWidget().setHtml(getState().getCaption()); + } else { + getWidget().setText(getState().getCaption()); } } diff --git a/client/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java b/client/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java index 7482748302..9b5c3cd767 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java +++ b/client/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java @@ -23,10 +23,18 @@ import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.event.logical.shared.ResizeEvent; +import com.google.gwt.event.logical.shared.ResizeHandler; +import com.google.gwt.http.client.Request; +import com.google.gwt.http.client.RequestBuilder; +import com.google.gwt.http.client.RequestCallback; +import com.google.gwt.http.client.RequestException; +import com.google.gwt.http.client.Response; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.History; +import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; @@ -84,6 +92,24 @@ public class RootConnector extends AbstractComponentContainerConnector com.google.gwt.user.client.Window.setTitle(title); } }); + final int heartbeatInterval = getState().getHeartbeatInterval(); + new Timer() { + @Override + public void run() { + sendHeartbeat(); + schedule(heartbeatInterval); + } + }.schedule(heartbeatInterval); + getWidget().addResizeHandler(new ResizeHandler() { + @Override + public void onResize(ResizeEvent event) { + rpc.resize(event.getHeight(), event.getWidth(), + Window.getClientWidth(), Window.getClientHeight()); + if (getState().isImmediate()) { + getConnection().sendPendingVariableChanges(); + } + } + }); } @Override @@ -442,4 +468,28 @@ public class RootConnector extends AbstractComponentContainerConnector }); } + private void sendHeartbeat() { + RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, "url"); + + rb.setCallback(new RequestCallback() { + + @Override + public void onResponseReceived(Request request, Response response) { + // TODO Auto-generated method stub + + } + + @Override + public void onError(Request request, Throwable exception) { + // TODO Auto-generated method stub + + } + }); + + try { + rb.send(); + } catch (RequestException re) { + + } + } } diff --git a/client/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java b/client/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java index a473bf4846..162e7c55a8 100644 --- a/client/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java +++ b/client/src/com/vaadin/terminal/gwt/client/ui/root/VRoot.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Element; +import com.google.gwt.event.logical.shared.HasResizeHandlers; import com.google.gwt.event.logical.shared.ResizeEvent; import com.google.gwt.event.logical.shared.ResizeHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; @@ -50,7 +51,8 @@ import com.vaadin.terminal.gwt.client.ui.textfield.VTextField; * */ public class VRoot extends SimplePanel implements ResizeHandler, - Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable { + Window.ClosingHandler, ShortcutActionHandlerOwner, Focusable, + HasResizeHandlers { private static final String CLASSNAME = "v-view"; @@ -401,16 +403,7 @@ public class VRoot extends SimplePanel implements ResizeHandler, int viewHeight = parentElement.getClientHeight(); int viewWidth = parentElement.getClientWidth(); - connection.updateVariable(id, "height", viewHeight, false); - connection.updateVariable(id, "width", viewWidth, false); - - int windowWidth = Window.getClientWidth(); - int windowHeight = Window.getClientHeight(); - - connection.updateVariable(id, RootConstants.BROWSER_WIDTH_VAR, - windowWidth, false); - connection.updateVariable(id, RootConstants.BROWSER_HEIGHT_VAR, - windowHeight, immediate); + ResizeEvent.fire(this, viewWidth, viewHeight); } public native static void goTo(String url) @@ -458,4 +451,9 @@ public class VRoot extends SimplePanel implements ResizeHandler, touchScrollHandler.addElement(getElement()); } + @Override + public HandlerRegistration addResizeHandler(ResizeHandler resizeHandler) { + return addHandler(resizeHandler, ResizeEvent.getType()); + } + } diff --git a/server/src/com/vaadin/Application.java b/server/src/com/vaadin/Application.java index 1d716d60c5..b120c8455a 100644 --- a/server/src/com/vaadin/Application.java +++ b/server/src/com/vaadin/Application.java @@ -361,31 +361,24 @@ public class Application implements Terminal.ErrorListener, Serializable { public static class ApplicationStartEvent implements Serializable { private final URL applicationUrl; - private final Properties applicationProperties; + private final DeploymentConfiguration configuration; private final ApplicationContext context; - private final boolean productionMode; - /** * @param applicationUrl * the URL the application should respond to. - * @param applicationProperties - * the Application properties as specified by the deployment - * configuration. + * @param configuration + * the deployment configuration for the application. * @param context * the context application will be running in. - * @param productionMode - * flag indicating whether the application is running in - * production mode. */ public ApplicationStartEvent(URL applicationUrl, - Properties applicationProperties, ApplicationContext context, - boolean productionMode) { + DeploymentConfiguration configuration, + ApplicationContext context) { this.applicationUrl = applicationUrl; - this.applicationProperties = applicationProperties; + this.configuration = configuration; this.context = context; - this.productionMode = productionMode; } /** @@ -401,15 +394,12 @@ public class Application implements Terminal.ErrorListener, Serializable { } /** - * Gets the Application properties as specified by the deployment - * configuration. - * - * @return the properties configured for the applciation. + * Returns the deployment configuration used by this application. * - * @see Application#getProperty(String) + * @return the deployment configuration. */ - public Properties getApplicationProperties() { - return applicationProperties; + public DeploymentConfiguration getConfiguration() { + return configuration; } /** @@ -422,18 +412,6 @@ public class Application implements Terminal.ErrorListener, Serializable { public ApplicationContext getContext() { return context; } - - /** - * Checks whether the application is running in production mode. - * - * @return <code>true</code> if in production mode, else - * <code>false</code> - * - * @see Application#isProductionMode() - */ - public boolean isProductionMode() { - return productionMode; - } } private final static Logger logger = Logger.getLogger(Application.class @@ -445,6 +423,11 @@ public class Application implements Terminal.ErrorListener, Serializable { private ApplicationContext context; /** + * Deployment configuration for the application. + */ + private DeploymentConfiguration configuration; + + /** * The current user or <code>null</code> if no user has logged in. */ private Object user; @@ -460,11 +443,6 @@ public class Application implements Terminal.ErrorListener, Serializable { private volatile boolean applicationIsRunning = false; /** - * Application properties. - */ - private Properties properties; - - /** * Default locale of the application. */ private Locale locale; @@ -512,8 +490,6 @@ public class Application implements Terminal.ErrorListener, Serializable { private int nextRootId = 0; private Map<Integer, Root> roots = new HashMap<Integer, Root>(); - private boolean productionMode = true; - private final Map<String, Integer> retainOnRefreshRoots = new HashMap<String, Integer>(); private final EventRouter eventRouter = new EventRouter(); @@ -638,8 +614,7 @@ public class Application implements Terminal.ErrorListener, Serializable { */ public void start(ApplicationStartEvent event) { applicationUrl = event.getApplicationUrl(); - productionMode = event.isProductionMode(); - properties = event.getApplicationProperties(); + configuration = event.getConfiguration(); context = event.getContext(); init(); applicationIsRunning = true; @@ -673,6 +648,16 @@ public class Application implements Terminal.ErrorListener, Serializable { } /** + * Returns the properties of this application as specified in the deployment + * configuration. + * + * @return Application properties + */ + protected Properties getProperties() { + return configuration.getInitParameters(); + } + + /** * Returns an enumeration of all the names in this application. * * <p> @@ -685,7 +670,7 @@ public class Application implements Terminal.ErrorListener, Serializable { * */ public Enumeration<?> getPropertyNames() { - return properties.propertyNames(); + return getProperties().propertyNames(); } /** @@ -700,7 +685,7 @@ public class Application implements Terminal.ErrorListener, Serializable { * @return the value in this property list with the specified key value. */ public String getProperty(String name) { - return properties.getProperty(name); + return getProperties().getProperty(name); } /** @@ -1930,7 +1915,7 @@ public class Application implements Terminal.ErrorListener, Serializable { * @since 7.0 */ protected String getRootClassName(WrappedRequest request) { - Object rootClassNameObj = properties.get(ROOT_PARAMETER); + Object rootClassNameObj = getProperties().get(ROOT_PARAMETER); if (rootClassNameObj instanceof String) { return (String) rootClassNameObj; } else { @@ -2181,7 +2166,7 @@ public class Application implements Terminal.ErrorListener, Serializable { * @since 7.0 */ public boolean isProductionMode() { - return productionMode; + return configuration.isProductionMode(); } /** diff --git a/server/src/com/vaadin/terminal/AbstractClientConnector.java b/server/src/com/vaadin/terminal/AbstractClientConnector.java index 0eb38a3d13..bc1cd2af1a 100644 --- a/server/src/com/vaadin/terminal/AbstractClientConnector.java +++ b/server/src/com/vaadin/terminal/AbstractClientConnector.java @@ -518,4 +518,9 @@ public abstract class AbstractClientConnector implements ClientConnector { return getParent().isConnectorEnabled(); } } + + @Override + public void beforeClientResponse(boolean initial) { + // Do nothing by default + } } diff --git a/server/src/com/vaadin/terminal/DeploymentConfiguration.java b/server/src/com/vaadin/terminal/DeploymentConfiguration.java index 74d0320ff8..14a5a3724f 100644 --- a/server/src/com/vaadin/terminal/DeploymentConfiguration.java +++ b/server/src/com/vaadin/terminal/DeploymentConfiguration.java @@ -132,4 +132,25 @@ public interface DeploymentConfiguration extends Serializable { public AddonContext getAddonContext(); public void setAddonContext(AddonContext vaadinContext); + + /** + * Returns whether Vaadin is in production mode. + * + * @return true if in production mode, false otherwise. + */ + public boolean isProductionMode(); + + /** + * Returns whether cross-site request forgery protection is enabled. + * + * @return true if XSRF protection is enabled, false otherwise. + */ + public boolean isXsrfProtectionEnabled(); + + /** + * Returns the time resources can be cached in the browsers, in seconds. + * + * @return The resource cache time. + */ + public int getResourceCacheTime(); } diff --git a/server/src/com/vaadin/terminal/Page.java b/server/src/com/vaadin/terminal/Page.java index d5d474e2e3..933f9b39e6 100644 --- a/server/src/com/vaadin/terminal/Page.java +++ b/server/src/com/vaadin/terminal/Page.java @@ -376,23 +376,17 @@ public class Page implements Serializable { .getBrowser(); } - public void setBrowserWindowSize(Integer width, Integer height) { + public void setBrowserWindowSize(int width, int height) { boolean fireEvent = false; - if (width != null) { - int newWidth = width.intValue(); - if (newWidth != browserWindowWidth) { - browserWindowWidth = newWidth; - fireEvent = true; - } + if (width != browserWindowWidth) { + browserWindowWidth = width; + fireEvent = true; } - if (height != null) { - int newHeight = height.intValue(); - if (newHeight != browserWindowHeight) { - browserWindowHeight = newHeight; - fireEvent = true; - } + if (height != browserWindowHeight) { + browserWindowHeight = height; + fireEvent = true; } if (fireEvent) { diff --git a/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java b/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java index 7892018218..bd39504237 100644 --- a/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java +++ b/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java @@ -217,99 +217,13 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet // TODO Can we close the application when the portlet is removed? Do we know // when the portlet is removed? - private boolean productionMode = false; - - private DeploymentConfiguration deploymentConfiguration = new AbstractDeploymentConfiguration( - getClass()) { - @Override - public String getConfiguredWidgetset(WrappedRequest request) { - - String widgetset = getApplicationOrSystemProperty( - PARAMETER_WIDGETSET, null); - - if (widgetset == null) { - // If no widgetset defined for the application, check the - // portal - // property - widgetset = WrappedPortletRequest.cast(request) - .getPortalProperty(PORTAL_PARAMETER_VAADIN_WIDGETSET); - } - - if (widgetset == null) { - // If no widgetset defined for the portal, use the default - widgetset = DEFAULT_WIDGETSET; - } - - return widgetset; - } - - @Override - public String getConfiguredTheme(WrappedRequest request) { - - // is the default theme defined by the portal? - String themeName = WrappedPortletRequest.cast(request) - .getPortalProperty(Constants.PORTAL_PARAMETER_VAADIN_THEME); - - if (themeName == null) { - // no, using the default theme defined by Vaadin - themeName = DEFAULT_THEME_NAME; - } - - return themeName; - } - - @Override - public boolean isStandalone(WrappedRequest request) { - return false; - } - - /* - * (non-Javadoc) - * - * @see - * com.vaadin.terminal.DeploymentConfiguration#getStaticFileLocation - * (com.vaadin.terminal.WrappedRequest) - * - * Return the URL from where static files, e.g. the widgetset and the - * theme, are served. In a standard configuration the VAADIN folder - * inside the returned folder is what is used for widgetsets and themes. - * - * @return The location of static resources (inside which there should - * be a VAADIN directory). Does not end with a slash (/). - */ - - @Override - public String getStaticFileLocation(WrappedRequest request) { - String staticFileLocation = WrappedPortletRequest.cast(request) - .getPortalProperty( - Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH); - if (staticFileLocation != null) { - // remove trailing slash if any - while (staticFileLocation.endsWith(".")) { - staticFileLocation = staticFileLocation.substring(0, - staticFileLocation.length() - 1); - } - return staticFileLocation; - } else { - // default for Liferay - return "/html"; - } - } - - @Override - public String getMimeType(String resourceName) { - return getPortletContext().getMimeType(resourceName); - } - }; - - private final AddonContext addonContext = new AddonContext( - getDeploymentConfiguration()); + private DeploymentConfiguration deploymentConfiguration; + private AddonContext addonContext; @Override public void init(PortletConfig config) throws PortletException { super.init(config); - Properties applicationProperties = getDeploymentConfiguration() - .getInitParameters(); + Properties applicationProperties = new Properties(); // Read default parameters from the context final PortletContext context = config.getPortletContext(); @@ -328,45 +242,101 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet config.getInitParameter(name)); } - checkProductionMode(); - checkCrossSiteProtection(); + deploymentConfiguration = new AbstractDeploymentConfiguration( + getClass(), applicationProperties) { + @Override + public String getConfiguredWidgetset(WrappedRequest request) { - addonContext.init(); - } + String widgetset = getApplicationOrSystemProperty( + PARAMETER_WIDGETSET, null); - @Override - public void destroy() { - super.destroy(); + if (widgetset == null) { + // If no widgetset defined for the application, check the + // portal property + widgetset = WrappedPortletRequest.cast(request) + .getPortalProperty( + PORTAL_PARAMETER_VAADIN_WIDGETSET); + } - addonContext.destroy(); - } + if (widgetset == null) { + // If no widgetset defined for the portal, use the default + widgetset = DEFAULT_WIDGETSET; + } + + return widgetset; + } + + @Override + public String getConfiguredTheme(WrappedRequest request) { + + // is the default theme defined by the portal? + String themeName = WrappedPortletRequest.cast(request) + .getPortalProperty( + Constants.PORTAL_PARAMETER_VAADIN_THEME); + + if (themeName == null) { + // no, using the default theme defined by Vaadin + themeName = DEFAULT_THEME_NAME; + } + + return themeName; + } + + @Override + public boolean isStandalone(WrappedRequest request) { + return false; + } - private void checkCrossSiteProtection() { - if (getDeploymentConfiguration().getApplicationOrSystemProperty( - SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION, "false").equals( - "true")) { /* - * Print an information/warning message about running with xsrf - * protection disabled + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.DeploymentConfiguration#getStaticFileLocation + * (com.vaadin.terminal.WrappedRequest) + * + * Return the URL from where static files, e.g. the widgetset and + * the theme, are served. In a standard configuration the VAADIN + * folder inside the returned folder is what is used for widgetsets + * and themes. + * + * @return The location of static resources (inside which there + * should be a VAADIN directory). Does not end with a slash (/). */ - getLogger().warning(WARNING_XSRF_PROTECTION_DISABLED); - } + + @Override + public String getStaticFileLocation(WrappedRequest request) { + String staticFileLocation = WrappedPortletRequest + .cast(request) + .getPortalProperty( + Constants.PORTAL_PARAMETER_VAADIN_RESOURCE_PATH); + if (staticFileLocation != null) { + // remove trailing slash if any + while (staticFileLocation.endsWith(".")) { + staticFileLocation = staticFileLocation.substring(0, + staticFileLocation.length() - 1); + } + return staticFileLocation; + } else { + // default for Liferay + return "/html"; + } + } + + @Override + public String getMimeType(String resourceName) { + return getPortletContext().getMimeType(resourceName); + } + }; + + addonContext = new AddonContext(deploymentConfiguration); + addonContext.init(); } - private void checkProductionMode() { - // TODO Identical code in AbstractApplicationServlet -> refactor - // Check if the application is in production mode. - // We are in production mode if productionMode=true - if (getDeploymentConfiguration().getApplicationOrSystemProperty( - SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals("true")) { - productionMode = true; - } + @Override + public void destroy() { + super.destroy(); - if (!productionMode) { - /* Print an information/warning message about running in debug mode */ - // TODO Maybe we need a different message for portlets? - getLogger().warning(NOT_PRODUCTION_MODE_INFO); - } + addonContext.destroy(); } protected enum RequestType { @@ -415,13 +385,13 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet } /** - * Returns true if the servlet is running in production mode. Production + * Returns true if the portlet is running in production mode. Production * mode disables all debug facilities. * * @return true if in production mode, false if in debug mode */ public boolean isProductionMode() { - return productionMode; + return deploymentConfiguration.isProductionMode(); } protected void handleRequest(PortletRequest request, @@ -811,8 +781,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet application.setLocale(locale); // No application URL when running inside a portlet application.start(new ApplicationStartEvent(null, - getDeploymentConfiguration().getInitParameters(), context, - isProductionMode())); + getDeploymentConfiguration(), context)); addonContext.fireApplicationStarted(application); } } diff --git a/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java b/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java index dcab8b44f5..062ba6cdf7 100644 --- a/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java +++ b/server/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java @@ -97,49 +97,11 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements // TODO Move some (all?) of the constants to a separate interface (shared // with portlet) - private boolean productionMode = false; - private final String resourcePath = null; - private int resourceCacheTime = 3600; - - private DeploymentConfiguration deploymentConfiguration = new AbstractDeploymentConfiguration( - getClass()) { - - @Override - public String getStaticFileLocation(WrappedRequest request) { - HttpServletRequest servletRequest = WrappedHttpServletRequest - .cast(request); - return AbstractApplicationServlet.this - .getStaticFilesLocation(servletRequest); - } - - @Override - public String getConfiguredWidgetset(WrappedRequest request) { - return getApplicationOrSystemProperty( - AbstractApplicationServlet.PARAMETER_WIDGETSET, - AbstractApplicationServlet.DEFAULT_WIDGETSET); - } - - @Override - public String getConfiguredTheme(WrappedRequest request) { - // Use the default - return AbstractApplicationServlet.getDefaultTheme(); - } - - @Override - public boolean isStandalone(WrappedRequest request) { - return true; - } + private DeploymentConfiguration deploymentConfiguration; - @Override - public String getMimeType(String resourceName) { - return getServletContext().getMimeType(resourceName); - } - }; - - private final AddonContext addonContext = new AddonContext( - getDeploymentConfiguration()); + private AddonContext addonContext; /** * Called by the servlet container to indicate to a servlet that the servlet @@ -156,8 +118,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements public void init(javax.servlet.ServletConfig servletConfig) throws javax.servlet.ServletException { super.init(servletConfig); - Properties applicationProperties = getDeploymentConfiguration() - .getInitParameters(); + Properties applicationProperties = new Properties(); // Read default parameters from server.xml final ServletContext context = servletConfig.getServletContext(); @@ -176,10 +137,42 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements servletConfig.getInitParameter(name)); } - checkProductionMode(); - checkCrossSiteProtection(); - checkResourceCacheTime(); + deploymentConfiguration = new AbstractDeploymentConfiguration( + getClass(), applicationProperties) { + + @Override + public String getStaticFileLocation(WrappedRequest request) { + HttpServletRequest servletRequest = WrappedHttpServletRequest + .cast(request); + return AbstractApplicationServlet.this + .getStaticFilesLocation(servletRequest); + } + + @Override + public String getConfiguredWidgetset(WrappedRequest request) { + return getApplicationOrSystemProperty( + AbstractApplicationServlet.PARAMETER_WIDGETSET, + AbstractApplicationServlet.DEFAULT_WIDGETSET); + } + + @Override + public String getConfiguredTheme(WrappedRequest request) { + // Use the default + return AbstractApplicationServlet.getDefaultTheme(); + } + + @Override + public boolean isStandalone(WrappedRequest request) { + return true; + } + @Override + public String getMimeType(String resourceName) { + return getServletContext().getMimeType(resourceName); + } + }; + + addonContext = new AddonContext(deploymentConfiguration); addonContext.init(); } @@ -190,47 +183,6 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements addonContext.destroy(); } - private void checkCrossSiteProtection() { - if (getDeploymentConfiguration().getApplicationOrSystemProperty( - SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION, "false").equals( - "true")) { - /* - * Print an information/warning message about running with xsrf - * protection disabled - */ - getLogger().warning(WARNING_XSRF_PROTECTION_DISABLED); - } - } - - private void checkProductionMode() { - // Check if the application is in production mode. - // We are in production mode if productionMode=true - if (getDeploymentConfiguration().getApplicationOrSystemProperty( - SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals("true")) { - productionMode = true; - } - - if (!productionMode) { - /* Print an information/warning message about running in debug mode */ - getLogger().warning(NOT_PRODUCTION_MODE_INFO); - } - - } - - private void checkResourceCacheTime() { - // Check if the browser caching time has been set in web.xml - try { - String rct = getDeploymentConfiguration() - .getApplicationOrSystemProperty( - SERVLET_PARAMETER_RESOURCE_CACHE_TIME, "3600"); - resourceCacheTime = Integer.parseInt(rct); - } catch (NumberFormatException nfe) { - // Default is 1h - resourceCacheTime = 3600; - getLogger().warning(WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC); - } - } - /** * Returns true if the servlet is running in production mode. Production * mode disables all debug facilities. @@ -238,17 +190,17 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements * @return true if in production mode, false if in debug mode */ public boolean isProductionMode() { - return productionMode; + return getDeploymentConfiguration().isProductionMode(); } /** - * Returns the amount of milliseconds the browser should cache a file. - * Default is 1 hour (3600 ms). + * Returns the number of seconds the browser should cache a file. Default is + * 1 hour (3600 s). * - * @return The amount of milliseconds files are cached in the browser + * @return The number of seconds files are cached in the browser */ public int getResourceCacheTime() { - return resourceCacheTime; + return getDeploymentConfiguration().getResourceCacheTime(); } /** @@ -909,8 +861,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements Locale locale = request.getLocale(); application.setLocale(locale); application.start(new ApplicationStartEvent(applicationUrl, - getDeploymentConfiguration().getInitParameters(), - webApplicationContext, isProductionMode())); + getDeploymentConfiguration(), webApplicationContext)); addonContext.fireApplicationStarted(application); } } @@ -1056,7 +1007,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements * parameter in web.xml */ response.setHeader("Cache-Control", - "max-age= " + String.valueOf(resourceCacheTime)); + "max-age= " + String.valueOf(getResourceCacheTime())); } // Write the resource to the client. diff --git a/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java b/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java index 00e65382cd..99376ffd1f 100644 --- a/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java +++ b/server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java @@ -68,7 +68,9 @@ import com.vaadin.external.json.JSONException; import com.vaadin.external.json.JSONObject; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.Connector; +import com.vaadin.shared.JavaScriptConnectorState; import com.vaadin.shared.Version; +import com.vaadin.shared.communication.LegacyChangeVariablesInvocation; import com.vaadin.shared.communication.MethodInvocation; import com.vaadin.shared.communication.SharedState; import com.vaadin.shared.communication.UidlValue; @@ -818,6 +820,7 @@ public abstract class AbstractCommunicationManager implements Serializable { if (repaintAll) { getClientCache(root).clear(); rootConnectorTracker.markAllConnectorsDirty(); + rootConnectorTracker.markAllClientSidesUninitialized(); // Reset sent locales locales = null; @@ -832,9 +835,9 @@ public abstract class AbstractCommunicationManager implements Serializable { "Found " + dirtyVisibleConnectors.size() + " dirty connectors to paint"); for (ClientConnector connector : dirtyVisibleConnectors) { - if (connector instanceof Component) { - ((Component) connector).updateState(); - } + boolean initialized = rootConnectorTracker + .isClientSideInitialized(connector); + connector.beforeClientResponse(!initialized); } rootConnectorTracker.markAllConnectorsClean(); @@ -883,23 +886,36 @@ public abstract class AbstractCommunicationManager implements Serializable { try { Class<? extends SharedState> stateType = connector .getStateType(); - SharedState referenceState = null; - if (repaintAll) { + Object diffState = rootConnectorTracker + .getDiffState(connector); + if (diffState == null) { + diffState = new JSONObject(); // Use an empty state object as reference for full // repaints - try { - referenceState = stateType.newInstance(); - } catch (Exception e) { - getLogger().log( - Level.WARNING, - "Error creating reference object for state of type " - + stateType.getName()); + boolean emptyInitialState = JavaScriptConnectorState.class + .isAssignableFrom(stateType); + if (!emptyInitialState) { + try { + SharedState referenceState = stateType + .newInstance(); + diffState = JsonCodec.encode(referenceState, + null, stateType, + root.getConnectorTracker()); + } catch (Exception e) { + getLogger().log( + Level.WARNING, + "Error creating reference object for state of type " + + stateType.getName()); + } } + rootConnectorTracker.setDiffState(connector, diffState); } - Object stateJson = JsonCodec.encode(state, referenceState, - stateType, root.getConnectorTracker()); + JSONObject stateJson = (JSONObject) JsonCodec.encode(state, + diffState, stateType, root.getConnectorTracker()); - sharedStates.put(connector.getConnectorId(), stateJson); + if (stateJson.length() != 0) { + sharedStates.put(connector.getConnectorId(), stateJson); + } } catch (JSONException e) { throw new PaintException( "Failed to serialize shared state for connector " @@ -1250,6 +1266,10 @@ public abstract class AbstractCommunicationManager implements Serializable { dragAndDropService.printJSONResponse(outWriter); } + for (ClientConnector connector : dirtyVisibleConnectors) { + rootConnectorTracker.markClientSideInitialized(connector); + } + writePerformanceData(outWriter); } diff --git a/server/src/com/vaadin/terminal/gwt/server/AbstractDeploymentConfiguration.java b/server/src/com/vaadin/terminal/gwt/server/AbstractDeploymentConfiguration.java index 33e1c43b38..ad5acad5e9 100644 --- a/server/src/com/vaadin/terminal/gwt/server/AbstractDeploymentConfiguration.java +++ b/server/src/com/vaadin/terminal/gwt/server/AbstractDeploymentConfiguration.java @@ -20,6 +20,7 @@ import java.lang.reflect.Constructor; import java.util.Iterator; import java.util.Properties; import java.util.ServiceLoader; +import java.util.logging.Logger; import com.vaadin.terminal.DeploymentConfiguration; @@ -27,11 +28,20 @@ public abstract class AbstractDeploymentConfiguration implements DeploymentConfiguration { private final Class<?> systemPropertyBaseClass; - private final Properties applicationProperties = new Properties(); + private final Properties applicationProperties; private AddonContext addonContext; + private boolean productionMode; + private boolean xsrfProtectionEnabled; + private int resourceCacheTime; - public AbstractDeploymentConfiguration(Class<?> systemPropertyBaseClass) { + public AbstractDeploymentConfiguration(Class<?> systemPropertyBaseClass, + Properties applicationProperties) { this.systemPropertyBaseClass = systemPropertyBaseClass; + this.applicationProperties = applicationProperties; + + checkProductionMode(); + checkXsrfProtection(); + checkResourceCacheTime(); } @Override @@ -152,4 +162,63 @@ public abstract class AbstractDeploymentConfiguration implements public AddonContext getAddonContext() { return addonContext; } + + @Override + public boolean isProductionMode() { + return productionMode; + } + + @Override + public boolean isXsrfProtectionEnabled() { + return xsrfProtectionEnabled; + } + + @Override + public int getResourceCacheTime() { + return resourceCacheTime; + } + + /** + * Log a warning if Vaadin is not running in production mode. + */ + private void checkProductionMode() { + productionMode = getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_PRODUCTION_MODE, "false").equals( + "true"); + if (!productionMode) { + getLogger().warning(Constants.NOT_PRODUCTION_MODE_INFO); + } + } + + /** + * Log a warning if cross-site request forgery protection is disabled. + */ + private void checkXsrfProtection() { + xsrfProtectionEnabled = !getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION, "false") + .equals("true"); + if (!xsrfProtectionEnabled) { + getLogger().warning(Constants.WARNING_XSRF_PROTECTION_DISABLED); + } + } + + /** + * Log a warning if resource cache time is set but is not an integer. + */ + private void checkResourceCacheTime() { + try { + resourceCacheTime = Integer + .parseInt(getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_RESOURCE_CACHE_TIME, + "3600")); + } catch (NumberFormatException e) { + getLogger().warning( + Constants.WARNING_RESOURCE_CACHING_TIME_NOT_NUMERIC); + resourceCacheTime = 3600; + } + } + + private Logger getLogger() { + return Logger.getLogger(getClass().getName()); + } } diff --git a/server/src/com/vaadin/terminal/gwt/server/ClientConnector.java b/server/src/com/vaadin/terminal/gwt/server/ClientConnector.java index eef4e240ec..c9fe2563f9 100644 --- a/server/src/com/vaadin/terminal/gwt/server/ClientConnector.java +++ b/server/src/com/vaadin/terminal/gwt/server/ClientConnector.java @@ -157,4 +157,24 @@ public interface ClientConnector extends Connector, RpcTarget { * attached to any Root */ public Root getRoot(); + + /** + * Called before the shared state and RPC invocations are sent to the + * client. Gives the connector an opportunity to set computed/dynamic state + * values or to invoke last minute RPC methods depending on other component + * features. + * <p> + * This method must not alter the component hierarchy in any way. Calling + * requestRepaint() from this method will have no effect. + * </p> + * + * @param initial + * <code>true</code> if the client-side connector will be created + * and initialized after this method has been invoked. + * <code>false</code> if there is already an initialized + * client-side connector. + * + * @since 7.0 + */ + public void beforeClientResponse(boolean initial); } diff --git a/server/src/com/vaadin/terminal/gwt/server/DragAndDropService.java b/server/src/com/vaadin/terminal/gwt/server/DragAndDropService.java index bb96c6e53e..56d5ed1393 100644 --- a/server/src/com/vaadin/terminal/gwt/server/DragAndDropService.java +++ b/server/src/com/vaadin/terminal/gwt/server/DragAndDropService.java @@ -322,4 +322,9 @@ public class DragAndDropService implements VariableOwner, ClientConnector { public Root getRoot() { return null; } + + @Override + public void beforeClientResponse(boolean initial) { + // Nothing to do + } } diff --git a/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java b/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java index 60197b0b3a..884e01f9a5 100644 --- a/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java +++ b/server/src/com/vaadin/terminal/gwt/server/JsonCodec.java @@ -165,6 +165,10 @@ public class JsonCodec implements Serializable { } else if (targetType == JSONObject.class || targetType == JSONArray.class) { return value; + } else if (Enum.class.isAssignableFrom(getClassForType(targetType))) { + Class<?> classForType = getClassForType(targetType); + return decodeEnum(classForType.asSubclass(Enum.class), + (String) value); } else { return decodeObject(targetType, (JSONObject) value, connectorTracker); @@ -420,9 +424,8 @@ public class JsonCodec implements Serializable { } } - private static Object decodeEnum(Class<? extends Enum> cls, JSONObject value) { - String enumIdentifier = String.valueOf(value); - return Enum.valueOf(cls, enumIdentifier); + private static Object decodeEnum(Class<? extends Enum> cls, String value) { + return Enum.valueOf(cls, value); } private static String[] decodeStringArray(JSONArray jsonArray) @@ -491,10 +494,6 @@ public class JsonCodec implements Serializable { throws JSONException { Class<?> targetClass = getClassForType(targetType); - if (Enum.class.isAssignableFrom(targetClass)) { - return decodeEnum(targetClass.asSubclass(Enum.class), - serializedObject); - } try { Object decodedObject = targetClass.newInstance(); @@ -527,9 +526,8 @@ public class JsonCodec implements Serializable { } } - public static Object encode(Object value, Object referenceValue, - Type valueType, ConnectorTracker connectorTracker) - throws JSONException { + public static Object encode(Object value, Object diffState, Type valueType, + ConnectorTracker connectorTracker) throws JSONException { if (valueType == null) { throw new IllegalArgumentException("type must be defined"); @@ -596,7 +594,7 @@ public class JsonCodec implements Serializable { } else { // Any object that we do not know how to encode we encode by looping // through fields - return encodeObject(value, referenceValue, connectorTracker); + return encodeObject(value, (JSONObject) diffState, connectorTracker); } } @@ -604,7 +602,7 @@ public class JsonCodec implements Serializable { return JSONObject.NULL; } - private static Object encodeObject(Object value, Object referenceValue, + private static Object encodeObject(Object value, JSONObject diffState, ConnectorTracker connectorTracker) throws JSONException { JSONObject jsonMap = new JSONObject(); @@ -621,10 +619,11 @@ public class JsonCodec implements Serializable { Type fieldType = getterMethod.getGenericReturnType(); Object fieldValue = getterMethod.invoke(value, (Object[]) null); boolean equals = false; - Object referenceFieldValue = null; - if (referenceValue != null) { - referenceFieldValue = getterMethod.invoke(referenceValue, - (Object[]) null); + Object diffStateValue = null; + if (diffState != null) { + diffStateValue = diffState.get(fieldName); + Object referenceFieldValue = decodeInternalOrCustomType( + fieldType, diffStateValue, connectorTracker); equals = equals(fieldValue, referenceFieldValue); } if (!equals) { @@ -638,8 +637,15 @@ public class JsonCodec implements Serializable { } jsonMap.put( fieldName, - encode(fieldValue, referenceFieldValue, fieldType, + encode(fieldValue, diffStateValue, fieldType, connectorTracker)); + if (diffState != null) { + diffState.put( + fieldName, + encode(fieldValue, null, fieldType, + connectorTracker)); + } + // } else { // System.out.println("Skipping field " + fieldName // + " of type " + fieldType.getName() diff --git a/server/src/com/vaadin/ui/AbsoluteLayout.java b/server/src/com/vaadin/ui/AbsoluteLayout.java index 9851a79bcd..a3bc577fe3 100644 --- a/server/src/com/vaadin/ui/AbsoluteLayout.java +++ b/server/src/com/vaadin/ui/AbsoluteLayout.java @@ -169,8 +169,8 @@ public class AbsoluteLayout extends AbstractLayout implements } @Override - public void updateState() { - super.updateState(); + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); // This could be in internalRemoveComponent and internalSetComponent if // Map<Connector,String> was supported. We cannot get the child diff --git a/server/src/com/vaadin/ui/AbstractComponent.java b/server/src/com/vaadin/ui/AbstractComponent.java index fb3993d0cf..cde5217ca1 100644 --- a/server/src/com/vaadin/ui/AbstractComponent.java +++ b/server/src/com/vaadin/ui/AbstractComponent.java @@ -717,13 +717,9 @@ public abstract class AbstractComponent extends AbstractClientConnector return (ComponentState) super.getState(); } - /* - * (non-Javadoc) - * - * @see com.vaadin.ui.Component#updateState() - */ @Override - public void updateState() { + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); // TODO This logic should be on the client side and the state should // simply be a data object with "width" and "height". if (getHeight() >= 0 diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 2d14acf442..67a1826100 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -1620,8 +1620,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements } @Override - public void updateState() { - super.updateState(); + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); // Hide the error indicator if needed getState().setHideErrors(shouldHideErrors()); diff --git a/server/src/com/vaadin/ui/AbstractTextField.java b/server/src/com/vaadin/ui/AbstractTextField.java index c8bbadd0ab..86315f801f 100644 --- a/server/src/com/vaadin/ui/AbstractTextField.java +++ b/server/src/com/vaadin/ui/AbstractTextField.java @@ -97,8 +97,8 @@ public abstract class AbstractTextField extends AbstractField<String> implements } @Override - public void updateState() { - super.updateState(); + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); String value = getValue(); if (value == null) { diff --git a/server/src/com/vaadin/ui/Component.java b/server/src/com/vaadin/ui/Component.java index ac668168f2..ff7ed47930 100644 --- a/server/src/com/vaadin/ui/Component.java +++ b/server/src/com/vaadin/ui/Component.java @@ -637,18 +637,6 @@ public interface Component extends ClientConnector, Sizeable, Serializable { public ComponentState getState(); /** - * Called before the shared state is sent to the client. Gives the component - * an opportunity to set computed/dynamic state values e.g. state values - * that depend on other component features. - * <p> - * This method must not alter the component hierarchy in any way. - * </p> - * - * @since 7.0 - */ - public void updateState(); - - /** * Adds an unique id for component that get's transferred to terminal for * testing purposes. Keeping identifiers unique is the responsibility of the * programmer. diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index 12ad377b62..2afe7f9025 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -20,6 +20,7 @@ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; @@ -39,7 +40,8 @@ import com.vaadin.terminal.gwt.server.ClientConnector; * Tracks which {@link ClientConnector}s are dirty so they can be updated to the * client when the following response is sent. A connector is dirty when an * operation has been performed on it on the server and as a result of this - * operation new information needs to be sent to its {@link ServerConnector}. + * operation new information needs to be sent to its + * {@link com.vaadin.terminal.gwt.client.ServerConnector}. * </p> * * @author Vaadin Ltd @@ -50,8 +52,10 @@ public class ConnectorTracker implements Serializable { private final HashMap<String, ClientConnector> connectorIdToConnector = new HashMap<String, ClientConnector>(); private Set<ClientConnector> dirtyConnectors = new HashSet<ClientConnector>(); + private Set<ClientConnector> uninitializedConnectors = new HashSet<ClientConnector>(); private Root root; + private Map<ClientConnector, Object> diffStates = new HashMap<ClientConnector, Object>(); /** * Gets a logger for this class @@ -91,6 +95,7 @@ public class ConnectorTracker implements Serializable { .get(connectorId); if (previouslyRegistered == null) { connectorIdToConnector.put(connectorId, connector); + uninitializedConnectors.add(connector); getLogger().fine( "Registered " + connector.getClass().getSimpleName() + " (" + connectorId + ")"); @@ -136,6 +141,49 @@ public class ConnectorTracker implements Serializable { "Unregistered " + connector.getClass().getSimpleName() + " (" + connectorId + ")"); connectorIdToConnector.remove(connectorId); + uninitializedConnectors.remove(connector); + diffStates.remove(connector); + } + + /** + * Checks whether the given connector has already been initialized in the + * browser. The given connector should be registered with this connector + * tracker. + * + * @param connector + * the client connector to check + * @return <code>true</code> if the initial state has previously been sent + * to the browser, <code>false</code> if the client-side doesn't + * already know anything about the connector. + */ + public boolean isClientSideInitialized(ClientConnector connector) { + assert connectorIdToConnector.get(connector.getConnectorId()) == connector : "Connector should be registered with this ConnectorTracker"; + return !uninitializedConnectors.contains(connector); + } + + /** + * Marks the given connector as initialized, meaning that the client-side + * state has been initialized for the connector. + * + * @see #isClientSideInitialized(ClientConnector) + * + * @param connector + * the connector that should be marked as initialized + */ + public void markClientSideInitialized(ClientConnector connector) { + uninitializedConnectors.remove(connector); + } + + /** + * Marks all currently registered connectors as uninitialized. This should + * be done when the client-side has been reset but the server-side state is + * retained. + * + * @see #isClientSideInitialized(ClientConnector) + */ + public void markAllClientSidesUninitialized() { + uninitializedConnectors.addAll(connectorIdToConnector.values()); + diffStates.clear(); } /** @@ -175,6 +223,8 @@ public class ConnectorTracker implements Serializable { "cleanConnectorMap unregistered connector " + getConnectorAndParentInfo(connector) + "). This should have been done when the connector was detached."); + uninitializedConnectors.remove(connector); + diffStates.remove(connector); iterator.remove(); } } @@ -327,4 +377,12 @@ public class ConnectorTracker implements Serializable { return dirtyConnectors; } + public Object getDiffState(ClientConnector connector) { + return diffStates.get(connector); + } + + public void setDiffState(ClientConnector connector, Object diffState) { + diffStates.put(connector, diffState); + } + } diff --git a/server/src/com/vaadin/ui/CssLayout.java b/server/src/com/vaadin/ui/CssLayout.java index c43f347e68..0192debc4a 100644 --- a/server/src/com/vaadin/ui/CssLayout.java +++ b/server/src/com/vaadin/ui/CssLayout.java @@ -197,8 +197,8 @@ public class CssLayout extends AbstractLayout implements LayoutClickNotifier { } @Override - public void updateState() { - super.updateState(); + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); getState().getChildCss().clear(); for (Iterator<Component> ci = getComponentIterator(); ci.hasNext();) { Component child = ci.next(); diff --git a/server/src/com/vaadin/ui/Root.java b/server/src/com/vaadin/ui/Root.java index 685296c55a..b37005a16e 100644 --- a/server/src/com/vaadin/ui/Root.java +++ b/server/src/com/vaadin/ui/Root.java @@ -434,6 +434,13 @@ public abstract class Root extends AbstractComponentContainer implements public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Root.this, mouseDetails)); } + + @Override + public void resize(int viewWidth, int viewHeight, int windowWidth, + int windowHeight) { + // TODO We're not doing anything with the view dimensions + getPage().setBrowserWindowSize(windowWidth, windowHeight); + } }; /** @@ -582,12 +589,6 @@ public abstract class Root extends AbstractComponentContainer implements .get(RootConstants.FRAGMENT_VARIABLE); getPage().setFragment(fragment, true); } - - if (variables.containsKey("height") || variables.containsKey("width")) { - getPage().setBrowserWindowSize((Integer) variables.get("width"), - (Integer) variables.get("height")); - } - } /* diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index 13ef7e5784..d1d2c25d8b 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -32,7 +32,6 @@ import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.event.ShortcutAction.ModifierKey; import com.vaadin.event.ShortcutListener; import com.vaadin.shared.MouseEventDetails; -import com.vaadin.shared.ui.root.RootConstants; import com.vaadin.shared.ui.window.WindowServerRpc; import com.vaadin.shared.ui.window.WindowState; import com.vaadin.terminal.PaintException; @@ -76,10 +75,6 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } }; - private int browserWindowWidth = -1; - - private int browserWindowHeight = -1; - /** * Creates a new unnamed window with a default layout. */ @@ -170,20 +165,6 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, .get("width") != getWidth())) { sizeHasChanged = true; } - Integer browserHeightVar = (Integer) variables - .get(RootConstants.BROWSER_HEIGHT_VAR); - if (browserHeightVar != null - && browserHeightVar.intValue() != browserWindowHeight) { - browserWindowHeight = browserHeightVar.intValue(); - sizeHasChanged = true; - } - Integer browserWidthVar = (Integer) variables - .get(RootConstants.BROWSER_WIDTH_VAR); - if (browserWidthVar != null - && browserWidthVar.intValue() != browserWindowWidth) { - browserWindowWidth = browserWidthVar.intValue(); - sizeHasChanged = true; - } super.changeVariables(source, variables); diff --git a/shared/src/com/vaadin/shared/annotations/Delayed.java b/shared/src/com/vaadin/shared/annotations/Delayed.java new file mode 100644 index 0000000000..706ffc1c53 --- /dev/null +++ b/shared/src/com/vaadin/shared/annotations/Delayed.java @@ -0,0 +1,54 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.shared.annotations; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +import com.vaadin.shared.communication.ServerRpc; + +/** + * Invoking a method in a {@link ServerRpc} interface marked with this + * annotation will only add the invocation to a queue of outgoing RPC + * invocations, but it will not cause the queue to be purged and sent to the + * server. The queue will instead be sent when any RPC method not marked as @Delayed + * has been invoked. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +@Target(ElementType.METHOD) +@Documented +public @interface Delayed { + /** + * By setting lastonly to <code>true</code>, any previous invocations of the + * same method will be removed from the queue when a new invocation is + * added. This can be used in cases where only the last value is of + * interest. + * <p> + * The default value is <code>false</code> which means that invoking the + * method multiple times will cause multiple invocations to be enqueued and + * eventually sent to the server. + * + * @return <code>true</code> if only the last invocation of the annotated + * method should be sent to the server, <code>false</code> if all + * enqueued invocations should be sent. + */ + public boolean lastonly() default false; +} diff --git a/server/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java b/shared/src/com/vaadin/shared/communication/LegacyChangeVariablesInvocation.java index fc3fbd6c00..2ffc56dd71 100644 --- a/server/src/com/vaadin/terminal/gwt/server/LegacyChangeVariablesInvocation.java +++ b/shared/src/com/vaadin/shared/communication/LegacyChangeVariablesInvocation.java @@ -13,13 +13,12 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.terminal.gwt.server; +package com.vaadin.shared.communication; import java.util.HashMap; import java.util.Map; import com.vaadin.shared.ApplicationConstants; -import com.vaadin.shared.communication.MethodInvocation; public class LegacyChangeVariablesInvocation extends MethodInvocation { private Map<String, Object> variableChanges = new HashMap<String, Object>(); @@ -27,7 +26,8 @@ public class LegacyChangeVariablesInvocation extends MethodInvocation { public LegacyChangeVariablesInvocation(String connectorId, String variableName, Object value) { super(connectorId, ApplicationConstants.UPDATE_VARIABLE_INTERFACE, - ApplicationConstants.UPDATE_VARIABLE_METHOD); + ApplicationConstants.UPDATE_VARIABLE_METHOD, new Object[] { + variableName, new UidlValue(value) }); setVariableChange(variableName, value); } @@ -47,4 +47,11 @@ public class LegacyChangeVariablesInvocation extends MethodInvocation { return variableChanges; } + @Override + public String getLastonlyTag() { + assert variableChanges.size() == 1; + return super.getLastonlyTag() + + variableChanges.keySet().iterator().next(); + } + } diff --git a/shared/src/com/vaadin/shared/communication/MethodInvocation.java b/shared/src/com/vaadin/shared/communication/MethodInvocation.java index 720ce09fcb..c4da937c27 100644 --- a/shared/src/com/vaadin/shared/communication/MethodInvocation.java +++ b/shared/src/com/vaadin/shared/communication/MethodInvocation.java @@ -71,4 +71,18 @@ public class MethodInvocation implements Serializable { + Arrays.toString(parameters) + ")"; } + /** + * Gets a String tag that is used to uniquely identify previous method + * invocations that should be purged from the queue if + * <code>{@literal @}Delay(lastonly = true)</code> is used. + * <p> + * The returned string should contain at least one non-number char to ensure + * it doesn't collide with the keys used for invocations without lastonly. + * + * @return a string identifying this method invocation + */ + public String getLastonlyTag() { + return connectorId + "-" + getInterfaceName() + "-" + getMethodName(); + } + }
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/root/RootConstants.java b/shared/src/com/vaadin/shared/ui/root/RootConstants.java index bc4f6017f6..34c17ac71f 100644 --- a/shared/src/com/vaadin/shared/ui/root/RootConstants.java +++ b/shared/src/com/vaadin/shared/ui/root/RootConstants.java @@ -21,12 +21,8 @@ public class RootConstants { */ @Deprecated public static final String RESIZE_LAZY = "rL"; - @Deprecated - public static final String BROWSER_HEIGHT_VAR = "browserHeight"; @Deprecated - public static final String BROWSER_WIDTH_VAR = "browserWidth"; - @Deprecated public static final String NOTIFICATION_HTML_CONTENT_NOT_ALLOWED = "useplain"; @Deprecated diff --git a/shared/src/com/vaadin/shared/ui/root/RootServerRpc.java b/shared/src/com/vaadin/shared/ui/root/RootServerRpc.java index f074a8d3cc..df2031f7d5 100644 --- a/shared/src/com/vaadin/shared/ui/root/RootServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/root/RootServerRpc.java @@ -15,9 +15,12 @@ */ package com.vaadin.shared.ui.root; +import com.vaadin.shared.annotations.Delayed; import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.ui.ClickRpc; public interface RootServerRpc extends ClickRpc, ServerRpc { - + @Delayed(lastonly = true) + public void resize(int viewWidth, int viewHeight, int windowWidth, + int windowHeight); }
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/root/RootState.java b/shared/src/com/vaadin/shared/ui/root/RootState.java index b7c2c88ce5..07c71c8167 100644 --- a/shared/src/com/vaadin/shared/ui/root/RootState.java +++ b/shared/src/com/vaadin/shared/ui/root/RootState.java @@ -20,6 +20,7 @@ import com.vaadin.shared.Connector; public class RootState extends ComponentState { private Connector content; + private int heartbeatInterval; public Connector getContent() { return content; @@ -29,4 +30,11 @@ public class RootState extends ComponentState { this.content = content; } + public int getHeartbeatInterval() { + return heartbeatInterval; + } + + public void setHeartbeatInterval(int heartbeatInterval) { + this.heartbeatInterval = heartbeatInterval; + } }
\ No newline at end of file diff --git a/tests/sass/src/com/vaadin/sass/testcases/scss/Comments.java b/tests/sass/src/com/vaadin/sass/testcases/scss/Comments.java index ea893f96e0..c76bbb8458 100644 --- a/tests/sass/src/com/vaadin/sass/testcases/scss/Comments.java +++ b/tests/sass/src/com/vaadin/sass/testcases/scss/Comments.java @@ -21,9 +21,6 @@ import java.net.URISyntaxException; import junit.framework.Assert; -import org.junit.Test; -import org.w3c.css.sac.CSSException; - import com.vaadin.sass.AbstractTestBase; import com.vaadin.sass.ScssStylesheet; import com.vaadin.sass.handler.SCSSDocumentHandler; @@ -31,6 +28,9 @@ import com.vaadin.sass.handler.SCSSDocumentHandlerImpl; import com.vaadin.sass.parser.Parser; import com.vaadin.sass.tree.CommentNode; +import org.junit.Test; +import org.w3c.css.sac.CSSException; + public class Comments extends AbstractTestBase { String scss = "/scss/comments.scss"; String css = "/css/comments.css"; diff --git a/tests/server-side/com/vaadin/tests/server/TransactionListenersConcurrency.java b/tests/server-side/com/vaadin/tests/server/TransactionListenersConcurrency.java index b567617fdd..f7ac55b6da 100644 --- a/tests/server-side/com/vaadin/tests/server/TransactionListenersConcurrency.java +++ b/tests/server-side/com/vaadin/tests/server/TransactionListenersConcurrency.java @@ -17,14 +17,15 @@ import javax.servlet.http.HttpSession; import junit.framework.TestCase; -import org.easymock.EasyMock; - import com.vaadin.Application; import com.vaadin.Application.ApplicationStartEvent; import com.vaadin.service.ApplicationContext.TransactionListener; +import com.vaadin.terminal.DeploymentConfiguration; import com.vaadin.terminal.gwt.server.AbstractWebApplicationContext; import com.vaadin.terminal.gwt.server.WebApplicationContext; +import org.easymock.EasyMock; + public class TransactionListenersConcurrency extends TestCase { /** @@ -71,10 +72,15 @@ public class TransactionListenersConcurrency extends TestCase { // Start the application so the transaction listener is // called later on. try { + DeploymentConfiguration dc = EasyMock + .createMock(DeploymentConfiguration.class); + EasyMock.expect(dc.isProductionMode()).andReturn(true); + EasyMock.expect(dc.getInitParameters()).andReturn( + new Properties()); + EasyMock.replay(dc); app.start(new ApplicationStartEvent(new URL( - "http://localhost/"), new Properties(), - context, true)); + "http://localhost/"), dc, context)); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -101,7 +107,9 @@ public class TransactionListenersConcurrency extends TestCase { @Override public void uncaughtException(Thread t, Throwable e) { - e = e.getCause(); + if (e.getCause() != null) { + e = e.getCause(); + } exceptions.add(e); } }); @@ -127,8 +135,10 @@ public class TransactionListenersConcurrency extends TestCase { if (t instanceof InvocationTargetException) { t = t.getCause(); } - t.printStackTrace(System.err); - fail(t.getClass().getName()); + if (t != null) { + t.printStackTrace(System.err); + fail(t.getClass().getName()); + } } System.out.println("Done, all ok"); diff --git a/tests/server-side/com/vaadin/tests/server/component/root/CustomRootClassLoader.java b/tests/server-side/com/vaadin/tests/server/component/root/CustomRootClassLoader.java index aa9753ebcc..fa730515a2 100644 --- a/tests/server-side/com/vaadin/tests/server/component/root/CustomRootClassLoader.java +++ b/tests/server-side/com/vaadin/tests/server/component/root/CustomRootClassLoader.java @@ -6,8 +6,6 @@ import java.util.Properties; import junit.framework.TestCase; -import org.easymock.EasyMock; - import com.vaadin.Application; import com.vaadin.Application.ApplicationStartEvent; import com.vaadin.RootRequiresMoreInformationException; @@ -15,6 +13,8 @@ import com.vaadin.terminal.DeploymentConfiguration; import com.vaadin.terminal.WrappedRequest; import com.vaadin.ui.Root; +import org.easymock.EasyMock; + public class CustomRootClassLoader extends TestCase { /** @@ -52,13 +52,24 @@ public class CustomRootClassLoader extends TestCase { */ public void testWithNullClassLoader() throws Exception { Application application = createStubApplication(); - application.start(new ApplicationStartEvent(null, new Properties(), - null, false)); + application.start(new ApplicationStartEvent(null, + createConfigurationMock(), null)); Root root = application.getRootForRequest(createRequestMock(null)); assertTrue(root instanceof MyRoot); } + private static DeploymentConfiguration createConfigurationMock() { + DeploymentConfiguration configurationMock = EasyMock + .createMock(DeploymentConfiguration.class); + EasyMock.expect(configurationMock.isProductionMode()).andReturn(false); + EasyMock.expect(configurationMock.getInitParameters()).andReturn( + new Properties()); + + EasyMock.replay(configurationMock); + return configurationMock; + } + private static WrappedRequest createRequestMock(ClassLoader classloader) { // Mock a DeploymentConfiguration to give the passed classloader DeploymentConfiguration configurationMock = EasyMock @@ -86,8 +97,8 @@ public class CustomRootClassLoader extends TestCase { LoggingClassLoader loggingClassLoader = new LoggingClassLoader(); Application application = createStubApplication(); - application.start(new ApplicationStartEvent(null, new Properties(), - null, false)); + application.start(new ApplicationStartEvent(null, + createConfigurationMock(), null)); Root root = application .getRootForRequest(createRequestMock(loggingClassLoader)); diff --git a/tests/testbench/com/vaadin/tests/serialization/SerializerNamespaceTest.java b/tests/testbench/com/vaadin/tests/serialization/SerializerNamespaceTest.java new file mode 100644 index 0000000000..6a873a6be3 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/serialization/SerializerNamespaceTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.serialization; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.terminal.WrappedRequest; +import com.vaadin.tests.components.AbstractTestRoot; +import com.vaadin.tests.widgetset.server.DummyLabel; +import com.vaadin.ui.Label; + +@Widgetset("com.vaadin.tests.widgetset.TestingWidgetSet") +public class SerializerNamespaceTest extends AbstractTestRoot { + + @Override + protected void setup(WrappedRequest request) { + addComponent(new Label("The real label")); + addComponent(new DummyLabel("The dummy label")); + } + + @Override + protected String getTestDescription() { + return "Using connectors with different state classes having the same simple name should not cause any clietn-side exceptions"; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(8683); + } + +} diff --git a/tests/testbench/com/vaadin/tests/serialization/SerializerTest.html b/tests/testbench/com/vaadin/tests/serialization/SerializerTest.html index 3d52356cab..4417fabf14 100644 --- a/tests/testbench/com/vaadin/tests/serialization/SerializerTest.html +++ b/tests/testbench/com/vaadin/tests/serialization/SerializerTest.html @@ -18,90 +18,94 @@ </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[16]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[17]</td> <td>1. sendBoolean: false, false, [false, false, true, false, true, true]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[15]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[16]</td> <td>2. sendByte: 5, -12, [3, 1, 2]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[14]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[15]</td> <td>3. sendChar: Å, ∫, [a, b, c, d]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[13]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[14]</td> <td>4. sendInt: 2, 5, [2147483647, 0]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[12]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[13]</td> <td>5. sendLong: -57841235865, 577431841358, [57, 0]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[11]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[12]</td> <td>6. sendFloat: 1.0000001, 3.14159, [-12.0, 0.0, 57.0]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[10]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[11]</td> <td>7. sendDouble: 0.423310825130748, 5.859874482048838, [2.0, 1.7976931348623157E308, 4.9E-324]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[9]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[10]</td> <td>8. sendString: Taegghiiiinnrsssstt‡</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[8]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[9]</td> <td>9. sendConnector: com.vaadin.tests.widgetset.server.SerializerTestExtension</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[7]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[8]</td> <td>10. sendBean: ComplexTestBean [innerBean1=SimpleTestBean(1), innerBean2=SimpleTestBean(3), innerBeanCollection=[SimpleTestBean(6), SimpleTestBean(0)], privimite=6], SimpleTestBean(0), [SimpleTestBean(7)]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[6]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[7]</td> <td>11. sendNull: null, Not null</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[5]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[6]</td> <td>12. sendNestedArray: [[7, 5]], [[SimpleTestBean(2)], [SimpleTestBean(4)]]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[4]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[5]</td> <td>13. sendList: [-234, 5, 8], class com.vaadin.tests.widgetset.server.SerializerTestExtension, class com.vaadin.tests.serialization.SerializerTest, [SimpleTestBean(-568), SimpleTestBean(234)]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[3]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[4]</td> <td>14. sendArrayList: [[2], [2]], [[2, 1], [2, 3]], [[SimpleTestBean(7)]]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[2]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[3]</td> <td>15. sendSet: [-12, -7, -4], class com.vaadin.tests.serialization.SerializerTest, [SimpleTestBean(2), SimpleTestBean(3)]</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[1]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[2]</td> <td>16. sendMap: {a=null}, [com.vaadin.tests.widgetset.server.SerializerTestExtension=false], [2=com.vaadin.tests.widgetset.server.SerializerTestExtension], {SimpleTestBean(4)=SimpleTestBean(-4), SimpleTestBean(-5)=SimpleTestBean(5)}</td> </tr> <tr> <td>assertText</td> - <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[0]</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[1]</td> <td>17. sendWrappedGenerics: {[SimpleTestBean(1)]={1=[SimpleTestBean(42)]}}</td> </tr> - +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsserializationSerializerTest::/VVerticalLayout[0]/VVerticalLayout[0]/VVerticalLayout[0]/VLabel[0]</td> + <td>18. sendEnum: PREFORMATTED, [XHTML, RAW], [PREFORMATTED, XML]</td> +</tr> </tbody></table> </body> </html> diff --git a/tests/testbench/com/vaadin/tests/serialization/SerializerTest.java b/tests/testbench/com/vaadin/tests/serialization/SerializerTest.java index da4b5dd7d9..a301ecf828 100644 --- a/tests/testbench/com/vaadin/tests/serialization/SerializerTest.java +++ b/tests/testbench/com/vaadin/tests/serialization/SerializerTest.java @@ -28,6 +28,7 @@ import java.util.Set; import com.vaadin.annotations.Widgetset; import com.vaadin.shared.Connector; +import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.terminal.WrappedRequest; import com.vaadin.tests.components.AbstractTestRoot; import com.vaadin.tests.util.Log; @@ -122,6 +123,10 @@ public class SerializerTest extends AbstractTestRoot { } }); + rpc.sendEnum(ContentMode.TEXT, new ContentMode[] { + ContentMode.PREFORMATTED, ContentMode.XML }, + Arrays.asList(ContentMode.XHTML, ContentMode.RAW)); + testExtension.registerRpc(new SerializerTestRpc() { @Override public void sendBoolean(boolean value, Boolean boxedValue, @@ -288,6 +293,13 @@ public class SerializerTest extends AbstractTestRoot { log.log("sendWrappedGenerics: " + generics.toString()); } + @Override + public void sendEnum(ContentMode contentMode, ContentMode[] array, + List<ContentMode> list) { + log.log("sendEnum: " + contentMode + ", " + + Arrays.toString(array) + ", " + list); + } + }); } diff --git a/tests/testbench/com/vaadin/tests/widgetset/client/DummyLabelConnector.java b/tests/testbench/com/vaadin/tests/widgetset/client/DummyLabelConnector.java new file mode 100644 index 0000000000..bf3a472b33 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/widgetset/client/DummyLabelConnector.java @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.widgetset.client; + +import com.vaadin.shared.ui.Connect; +import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; +import com.vaadin.terminal.gwt.client.ui.AbstractComponentConnector; +import com.vaadin.terminal.gwt.client.ui.label.VLabel; +import com.vaadin.tests.widgetset.server.DummyLabel; + +/** + * Dummy connector just to cause {@link LabelState} to be used to test #8683 + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +@Connect(DummyLabel.class) +public class DummyLabelConnector extends AbstractComponentConnector { + @Override + public LabelState getState() { + return (LabelState) super.getState(); + } + + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + + getWidget().setText(getState().getText()); + } + + @Override + public VLabel getWidget() { + return (VLabel) super.getWidget(); + } +} diff --git a/tests/testbench/com/vaadin/tests/widgetset/client/LabelState.java b/tests/testbench/com/vaadin/tests/widgetset/client/LabelState.java new file mode 100644 index 0000000000..91a269e33f --- /dev/null +++ b/tests/testbench/com/vaadin/tests/widgetset/client/LabelState.java @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.widgetset.client; + +import com.vaadin.shared.ComponentState; + +/** + * State class with the same simple name as + * {@link com.vaadin.shared.ui.label.LabelState} to test #8683 + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class LabelState extends ComponentState { + + private String text; + + public void setText(String text) { + this.text = text; + } + + public String getText() { + return text; + } + +} diff --git a/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestConnector.java b/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestConnector.java index 3977ff7f71..9fb6807bb7 100644 --- a/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestConnector.java +++ b/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestConnector.java @@ -28,6 +28,7 @@ import java.util.Set; import com.vaadin.shared.Connector; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.terminal.gwt.client.communication.RpcProxy; import com.vaadin.terminal.gwt.client.communication.StateChangeEvent; import com.vaadin.terminal.gwt.client.extensions.AbstractExtensionConnector; @@ -231,6 +232,16 @@ public class SerializerTestConnector extends AbstractExtensionConnector { objectListArray[0] }, new List[] { Collections .singletonList(beanListArray[0].get(0)) }); } + + @Override + public void sendEnum(ContentMode contentMode, ContentMode[] array, + List<ContentMode> list) { + ContentMode nextContentMode = ContentMode.values()[contentMode + .ordinal() + 1]; + rpc.sendEnum(nextContentMode, + list.toArray(new ContentMode[list.size()]), + Arrays.asList(array)); + } }); } diff --git a/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestRpc.java b/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestRpc.java index 47a26b7408..eb3a20e90e 100644 --- a/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestRpc.java +++ b/tests/testbench/com/vaadin/tests/widgetset/client/SerializerTestRpc.java @@ -23,6 +23,7 @@ import java.util.Set; import com.vaadin.shared.Connector; import com.vaadin.shared.communication.ClientRpc; import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.shared.ui.label.ContentMode; @SuppressWarnings("javadoc") public interface SerializerTestRpc extends ServerRpc, ClientRpc { @@ -73,4 +74,6 @@ public interface SerializerTestRpc extends ServerRpc, ClientRpc { public void sendWrappedGenerics( Map<Set<SimpleTestBean>, Map<Integer, List<SimpleTestBean>>> generics); + public void sendEnum(ContentMode contentMode, ContentMode[] array, List<ContentMode> list); + } diff --git a/tests/testbench/com/vaadin/tests/widgetset/server/DummyLabel.java b/tests/testbench/com/vaadin/tests/widgetset/server/DummyLabel.java new file mode 100644 index 0000000000..bda36d64a8 --- /dev/null +++ b/tests/testbench/com/vaadin/tests/widgetset/server/DummyLabel.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.widgetset.server; + +import com.vaadin.tests.widgetset.client.LabelState; +import com.vaadin.ui.AbstractComponent; + +/** + * Dummy component to cause {@link LabelState} to be used to test #8683 + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.0.0 + */ +public class DummyLabel extends AbstractComponent { + public DummyLabel(String text) { + getState().setText(text); + } + + @Override + public LabelState getState() { + return (LabelState) super.getState(); + } +} |