diff options
author | Artur Signell <artur@vaadin.com> | 2015-10-22 15:13:27 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2016-03-05 11:20:23 +0000 |
commit | 39640d188b0c7b9964d5df34f97a83839f2b3732 (patch) | |
tree | b18f34910bd9d16aadb7fb003c7be5d37b707525 | |
parent | 16f9434bee7a8eebaf580d82db25a47e8b24bbdd (diff) | |
download | vaadin-framework-39640d188b0c7b9964d5df34f97a83839f2b3732.tar.gz vaadin-framework-39640d188b0c7b9964d5df34f97a83839f2b3732.zip |
Make it possible to intercept RPC calls without copy/paste
Change-Id: I50c2e4a95b492a42c4291e8dcbede3de87f1be6f
8 files changed, 412 insertions, 33 deletions
diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index 1879175109..0def4001b8 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -109,6 +109,15 @@ </init-param> <async-supported>true</async-supported> </servlet> + <servlet> + <servlet-name>RPCLogger</servlet-name> + <servlet-class>com.vaadin.tests.rpclogger.RPCLoggerServlet</servlet-class> + <init-param> + <param-name>ui</param-name> + <param-value>com.vaadin.tests.rpclogger.RPCLoggerUI</param-value> + </init-param> + <async-supported>true</async-supported> + </servlet> <servlet> <!-- This servlet is a separate instance for the sole purpose of @@ -203,6 +212,11 @@ </servlet-mapping> <servlet-mapping> + <servlet-name>RPCLogger</servlet-name> + <url-pattern>/rpclogger/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> <servlet-name>VaadinStaticFiles</servlet-name> <url-pattern>/VAADIN/*</url-pattern> </servlet-mapping> diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java index 593b1f544f..3503a2d8b1 100644 --- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java +++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java @@ -316,7 +316,7 @@ public class ServerRpcHandler implements Serializable { * 6 semantics for components and add-ons that do not use Vaadin 7 RPC * directly. * - * @param uI + * @param ui * the UI receiving the invocations data * @param lastSyncIdSeenByClient * the most recent sync id the client has seen at the time the @@ -324,20 +324,21 @@ public class ServerRpcHandler implements Serializable { * @param invocationsData * JSON containing all information needed to execute all * requested RPC calls. + * @since */ - private void handleInvocations(UI uI, int lastSyncIdSeenByClient, + protected void handleInvocations(UI ui, int lastSyncIdSeenByClient, JsonArray invocationsData) { // TODO PUSH Refactor so that this is not needed - LegacyCommunicationManager manager = uI.getSession() + LegacyCommunicationManager manager = ui.getSession() .getCommunicationManager(); try { - ConnectorTracker connectorTracker = uI.getConnectorTracker(); + ConnectorTracker connectorTracker = ui.getConnectorTracker(); Set<Connector> enabledConnectors = new HashSet<Connector>(); List<MethodInvocation> invocations = parseInvocations( - uI.getConnectorTracker(), invocationsData, + ui.getConnectorTracker(), invocationsData, lastSyncIdSeenByClient); for (MethodInvocation invocation : invocations) { final ClientConnector connector = connectorTracker @@ -404,35 +405,11 @@ public class ServerRpcHandler implements Serializable { } if (invocation instanceof ServerRpcMethodInvocation) { - try { - ServerRpcManager.applyInvocation(connector, - (ServerRpcMethodInvocation) invocation); - } catch (RpcInvocationException e) { - manager.handleConnectorRelatedException(connector, e); - } + handleInvocation(ui, connector, + (ServerRpcMethodInvocation) invocation); } else { - - // All code below is for legacy variable changes LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; - Map<String, Object> changes = legacyInvocation - .getVariableChanges(); - try { - if (connector instanceof VariableOwner) { - // The source parameter is never used anywhere - changeVariables(null, (VariableOwner) connector, - changes); - } else { - throw new IllegalStateException( - "Received legacy variable change for " - + connector.getClass().getName() - + " (" - + connector.getConnectorId() - + ") which is not a VariableOwner. The client-side connector sent these legacy varaibles: " - + changes.keySet()); - } - } catch (Exception e) { - manager.handleConnectorRelatedException(connector, e); - } + handleInvocation(ui, connector, legacyInvocation); } } } catch (JsonException e) { @@ -444,6 +421,63 @@ public class ServerRpcHandler implements Serializable { } /** + * Handles the given RPC method invocation for the given connector + * + * @since + * @param ui + * the UI containing the connector + * @param connector + * the connector the RPC is targeted to + * @param invocation + * information about the rpc to invoke + */ + protected void handleInvocation(UI ui, ClientConnector connector, + ServerRpcMethodInvocation invocation) { + try { + ServerRpcManager.applyInvocation(connector, invocation); + } catch (RpcInvocationException e) { + ui.getSession().getCommunicationManager() + .handleConnectorRelatedException(connector, e); + } + + } + + /** + * Handles the given Legacy variable change RPC method invocation for the + * given connector + * + * @since + * @param ui + * the UI containing the connector + * @param connector + * the connector the RPC is targeted to + * @param invocation + * information about the rpc to invoke + */ + protected void handleInvocation(UI ui, ClientConnector connector, + LegacyChangeVariablesInvocation legacyInvocation) { + Map<String, Object> changes = legacyInvocation.getVariableChanges(); + try { + if (connector instanceof VariableOwner) { + // The source parameter is never used anywhere + changeVariables(null, (VariableOwner) connector, changes); + } else { + throw new IllegalStateException( + "Received legacy variable change for " + + connector.getClass().getName() + + " (" + + connector.getConnectorId() + + ") which is not a VariableOwner. The client-side connector sent these legacy varaibles: " + + changes.keySet()); + } + } catch (Exception e) { + ui.getSession().getCommunicationManager() + .handleConnectorRelatedException(connector, e); + } + + } + + /** * Parse JSON from the client into a list of MethodInvocation instances. * * @param connectorTracker diff --git a/server/src/com/vaadin/server/communication/UidlRequestHandler.java b/server/src/com/vaadin/server/communication/UidlRequestHandler.java index dda3d81453..db18bb9e1e 100644 --- a/server/src/com/vaadin/server/communication/UidlRequestHandler.java +++ b/server/src/com/vaadin/server/communication/UidlRequestHandler.java @@ -51,9 +51,20 @@ public class UidlRequestHandler extends SynchronizedRequestHandler implements public static final String UIDL_PATH = "UIDL/"; - private ServerRpcHandler rpcHandler = new ServerRpcHandler(); + private ServerRpcHandler rpcHandler; public UidlRequestHandler() { + rpcHandler = createRpcHandler(); + } + + /** + * Creates the ServerRpcHandler to use + * + * @since + * @return the ServerRpcHandler to use + */ + protected ServerRpcHandler createRpcHandler() { + return new ServerRpcHandler(); } @Override diff --git a/uitest/src/com/vaadin/tests/rpclogger/LoggingServerRpcHandler.java b/uitest/src/com/vaadin/tests/rpclogger/LoggingServerRpcHandler.java new file mode 100644 index 0000000000..1cb66972ef --- /dev/null +++ b/uitest/src/com/vaadin/tests/rpclogger/LoggingServerRpcHandler.java @@ -0,0 +1,40 @@ +/* + * Copyright 2000-2014 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.rpclogger; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.ServerRpcMethodInvocation; +import com.vaadin.server.communication.ServerRpcHandler; +import com.vaadin.shared.communication.LegacyChangeVariablesInvocation; +import com.vaadin.ui.UI; + +public class LoggingServerRpcHandler extends ServerRpcHandler { + + @Override + protected void handleInvocation(UI ui, ClientConnector connector, + LegacyChangeVariablesInvocation legacyInvocation) { + ((RPCLoggerUI) ui).recordInvocation(connector, legacyInvocation); + super.handleInvocation(ui, connector, legacyInvocation); + } + + @Override + protected void handleInvocation(UI ui, ClientConnector connector, + ServerRpcMethodInvocation invocation) { + ((RPCLoggerUI) ui).recordInvocation(connector, invocation); + super.handleInvocation(ui, connector, invocation); + } + +} diff --git a/uitest/src/com/vaadin/tests/rpclogger/LoggingUidlRequestHandler.java b/uitest/src/com/vaadin/tests/rpclogger/LoggingUidlRequestHandler.java new file mode 100644 index 0000000000..5eff9b56d9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/rpclogger/LoggingUidlRequestHandler.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2014 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.rpclogger; + +import com.vaadin.server.communication.ServerRpcHandler; +import com.vaadin.server.communication.UidlRequestHandler; + +public class LoggingUidlRequestHandler extends UidlRequestHandler { + + @Override + protected ServerRpcHandler createRpcHandler() { + return new LoggingServerRpcHandler(); + } + +} diff --git a/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerService.java b/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerService.java new file mode 100644 index 0000000000..a70a51b603 --- /dev/null +++ b/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerService.java @@ -0,0 +1,41 @@ +/* + * Copyright 2000-2014 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.rpclogger; + +import java.util.List; + +import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.RequestHandler; +import com.vaadin.server.ServiceException; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletService; + +public class RPCLoggerService extends VaadinServletService { + + public RPCLoggerService(VaadinServlet servlet, + DeploymentConfiguration deploymentConfiguration) + throws ServiceException { + super(servlet, deploymentConfiguration); + } + + @Override + protected List<RequestHandler> createRequestHandlers() + throws ServiceException { + List<RequestHandler> handlers = super.createRequestHandlers(); + handlers.add(new LoggingUidlRequestHandler()); + return handlers; + } +} diff --git a/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerServlet.java b/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerServlet.java new file mode 100644 index 0000000000..adb4302ba3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerServlet.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2014 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.rpclogger; + +import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.server.ServiceException; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletService; + +public class RPCLoggerServlet extends VaadinServlet { + + @Override + protected VaadinServletService createServletService( + DeploymentConfiguration deploymentConfiguration) + throws ServiceException { + RPCLoggerService service = new RPCLoggerService(this, + deploymentConfiguration); + service.init(); + return service; + } +} diff --git a/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerUI.java b/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerUI.java new file mode 100644 index 0000000000..bac82f232d --- /dev/null +++ b/uitest/src/com/vaadin/tests/rpclogger/RPCLoggerUI.java @@ -0,0 +1,177 @@ +/* + * Copyright 2000-2014 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.rpclogger; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.ErrorHandler; +import com.vaadin.server.ServerRpcMethodInvocation; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.communication.LegacyChangeVariablesInvocation; +import com.vaadin.shared.communication.MethodInvocation; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.AbstractComponent; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.ListSelect; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Notification.Type; + +public class RPCLoggerUI extends AbstractTestUIWithLog implements ErrorHandler { + + private List<Action> lastActions = new ArrayList<Action>(); + + public static class Action { + public Action(ClientConnector connector, MethodInvocation invocation) { + target = connector; + this.invocation = invocation; + } + + private MethodInvocation invocation; + private ClientConnector target; + } + + @Override + protected int getLogSize() { + return 10; + } + + @Override + protected void setup(VaadinRequest request) { + setErrorHandler(this); + addComponent(new Button("Do something")); + ListSelect s = new ListSelect(); + s.setMultiSelect(true); + s.addItem("foo"); + s.addItem("bar"); + addComponent(s); + + addComponent(new Button("Action, which will fail", new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + throw new RuntimeException("Something went wrong"); + } + })); + } + + public void recordInvocation(ClientConnector connector, + LegacyChangeVariablesInvocation legacyInvocation) { + addAction(new Action(connector, legacyInvocation)); + } + + public void recordInvocation(ClientConnector connector, + ServerRpcMethodInvocation invocation) { + addAction(new Action(connector, invocation)); + } + + private void addAction(Action action) { + while (lastActions.size() >= 5) { + lastActions.remove(0); + } + lastActions.add(action); + } + + public String formatAction(ClientConnector connector, + LegacyChangeVariablesInvocation legacyInvocation) { + String connectorIdentifier = getConnectorIdentifier(connector); + Map<String, Object> changes = legacyInvocation.getVariableChanges(); + String rpcInfo = ""; + for (String key : changes.keySet()) { + Object value = changes.get(key); + rpcInfo += key + ": " + formatValue(value); + } + return "Legacy RPC " + rpcInfo + " for " + connectorIdentifier; + + } + + public String formatAction(ClientConnector connector, + ServerRpcMethodInvocation invocation) { + String connectorIdentifier = getConnectorIdentifier(connector); + String rpcInfo = invocation.getInterfaceName() + "." + + invocation.getMethodName() + " ("; + for (Object o : invocation.getParameters()) { + rpcInfo += formatValue(o); + rpcInfo += ","; + } + rpcInfo = rpcInfo.substring(0, rpcInfo.length() - 2) + ")"; + return "RPC " + rpcInfo + " for " + connectorIdentifier; + + } + + private String formatValue(Object value) { + if (value == null) { + return "null"; + } + if (value instanceof String) { + return (String) value; + } else if (value instanceof Object[]) { + String formatted = ""; + for (Object o : ((Object[]) value)) { + formatted += formatValue(o) + ","; + } + return formatted; + } else { + return value.toString(); + } + } + + private String getConnectorIdentifier(ClientConnector connector) { + String connectorIdentifier = connector.getClass().getSimpleName(); + if (connector instanceof AbstractComponent) { + String caption = ((AbstractComponent) connector).getCaption(); + if (caption != null) { + connectorIdentifier += " - " + caption; + } + } + return "'" + connectorIdentifier + "'"; + } + + @Override + public void error(com.vaadin.server.ErrorEvent event) { + String msg = ""; + + for (int i = 0; i < lastActions.size(); i++) { + Action action = lastActions.get(i); + if (action.invocation instanceof ServerRpcMethodInvocation) { + msg += "\n" + + (i + 1) + + " " + + formatAction(action.target, + (ServerRpcMethodInvocation) action.invocation); + } else { + msg += "\n" + + (i + 1) + + " " + + formatAction( + action.target, + (LegacyChangeVariablesInvocation) action.invocation); + } + } + + msg += "\n"; + msg += "\n"; + msg += "This error should not really be shown but logged for later analysis."; + Notification.show( + "Something went wrong. Actions leading up to this error were:", + msg, Type.ERROR_MESSAGE); + // log(msg); + } +} |