diff options
author | Johannes Dahlström <johannesd@vaadin.com> | 2012-11-20 19:04:55 +0200 |
---|---|---|
committer | Johannes Dahlström <johannesd@vaadin.com> | 2012-11-21 15:48:44 +0200 |
commit | 741758d24ff4803ce674eacf79a3ecaaa8f26ed6 (patch) | |
tree | 2b15446ac053a7d1504cb5379474f2be8556375b | |
parent | 5085308ae1619f8c68900379077b581df526eb94 (diff) | |
download | vaadin-framework-741758d24ff4803ce674eacf79a3ecaaa8f26ed6.tar.gz vaadin-framework-741758d24ff4803ce674eacf79a3ecaaa8f26ed6.zip |
Add attach and detach listeners and events to ClientConnector (#8579, #10251)
Change-Id: Ifda4b4a770fb7d330f0fe5d606f8415961a574a2
5 files changed, 308 insertions, 13 deletions
diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index 45c6da7e77..8b5bd78d01 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -92,6 +92,30 @@ public abstract class AbstractClientConnector implements ClientConnector, private ErrorHandler errorHandler = null; + @Override + public void addAttachListener(AttachListener listener) { + addListener(AttachEvent.ATTACH_EVENT_IDENTIFIER, AttachEvent.class, + listener, AttachListener.attachMethod); + } + + @Override + public void removeAttachListener(AttachListener listener) { + removeListener(AttachEvent.ATTACH_EVENT_IDENTIFIER, AttachEvent.class, + listener); + } + + @Override + public void addDetachListener(DetachListener listener) { + addListener(DetachEvent.DETACH_EVENT_IDENTIFIER, DetachEvent.class, + listener, DetachListener.detachMethod); + } + + @Override + public void removeDetachListener(DetachListener listener) { + removeListener(DetachEvent.DETACH_EVENT_IDENTIFIER, DetachEvent.class, + listener); + } + /** * @deprecated As of 7.0.0, use {@link #markAsDirty()} instead */ @@ -568,10 +592,11 @@ public abstract class AbstractClientConnector implements ClientConnector, getUI().getConnectorTracker().registerConnector(this); + fireEvent(new AttachEvent(this)); + for (ClientConnector connector : getAllChildrenIterable(this)) { connector.attach(); } - } /** @@ -588,6 +613,8 @@ public abstract class AbstractClientConnector implements ClientConnector, connector.detach(); } + fireEvent(new DetachEvent(this)); + getUI().getConnectorTracker().unregisterConnector(this); } @@ -950,7 +977,6 @@ public abstract class AbstractClientConnector implements ClientConnector, if (eventRouter != null) { eventRouter.fireEvent(event); } - } /* @@ -971,5 +997,4 @@ public abstract class AbstractClientConnector implements ClientConnector, public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } - } diff --git a/server/src/com/vaadin/server/ClientConnector.java b/server/src/com/vaadin/server/ClientConnector.java index 440f282508..4a09158da5 100644 --- a/server/src/com/vaadin/server/ClientConnector.java +++ b/server/src/com/vaadin/server/ClientConnector.java @@ -16,7 +16,11 @@ package com.vaadin.server; import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Method; import java.util.Collection; +import java.util.EventListener; +import java.util.EventObject; import java.util.List; import org.json.JSONException; @@ -27,6 +31,7 @@ import com.vaadin.shared.communication.SharedState; import com.vaadin.ui.Component; import com.vaadin.ui.ComponentContainer; import com.vaadin.ui.UI; +import com.vaadin.util.ReflectTools; /** * Interface implemented by all connectors that are capable of communicating @@ -37,6 +42,81 @@ import com.vaadin.ui.UI; * */ public interface ClientConnector extends Connector { + + public static abstract class ConnectorEvent extends EventObject { + public ConnectorEvent(ClientConnector source) { + super(source); + } + + public ClientConnector getConnector() { + return (ClientConnector) getSource(); + } + } + + /** + * Event fired after a connector is attached to the application. + */ + public static class AttachEvent extends ConnectorEvent { + public static final String ATTACH_EVENT_IDENTIFIER = "clientConnectorAttach"; + + public AttachEvent(ClientConnector source) { + super(source); + } + } + + /** + * Interface for listening {@link DetachEvent connector detach events}. + * + */ + public static interface AttachListener extends EventListener, Serializable { + public static final Method attachMethod = ReflectTools.findMethod( + AttachListener.class, "attach", AttachEvent.class); + + /** + * Called when a AttachListener is notified of a AttachEvent. + * + * @param event + * The attach event that was fired. + */ + public void attach(AttachEvent event); + } + + /** + * Event fired before a connector is detached from the application. + */ + public static class DetachEvent extends ConnectorEvent { + public static final String DETACH_EVENT_IDENTIFIER = "clientConnectorDetach"; + + public DetachEvent(ClientConnector source) { + super(source); + } + } + + /** + * Interface for listening {@link DetachEvent connector detach events}. + * + */ + public static interface DetachListener extends EventListener, Serializable { + public static final Method detachMethod = ReflectTools.findMethod( + DetachListener.class, "detach", DetachEvent.class); + + /** + * Called when a DetachListener is notified of a DetachEvent. + * + * @param event + * The detach event that was fired. + */ + public void detach(DetachEvent event); + } + + public void addAttachListener(AttachListener listener); + + public void removeAttachListener(AttachListener listener); + + public void addDetachListener(DetachListener listener); + + public void removeDetachListener(DetachListener listener); + /** * An error event for connector related errors. Use {@link #getConnector()} * to find the connector where the error occurred or {@link #getComponent()} @@ -174,13 +254,13 @@ public interface ClientConnector extends Connector { public void attach(); /** - * Notifies the component that it is detached from the application. + * Notifies the connector that it is detached from the application. * * <p> * The caller of this method is {@link #setParent(ClientConnector)} if the * parent is in the application. When the parent is detached from the - * application it is its response to call {@link #detach()} for all the - * children and to detach itself from the terminal. + * application it is its responsibility to call {@link #detach()} for each + * of its children. * </p> */ public void detach(); diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java index 0fa39fa0e8..529cce670e 100644 --- a/server/src/com/vaadin/server/DragAndDropService.java +++ b/server/src/com/vaadin/server/DragAndDropService.java @@ -358,4 +358,20 @@ public class DragAndDropService implements VariableOwner, ClientConnector { public void setErrorHandler(ErrorHandler errorHandler) { this.errorHandler = errorHandler; } + + @Override + public void addAttachListener(AttachListener listener) { + } + + @Override + public void removeAttachListener(AttachListener listener) { + } + + @Override + public void addDetachListener(DetachListener listener) { + } + + @Override + public void removeDetachListener(DetachListener listener) { + } } diff --git a/server/src/com/vaadin/ui/Component.java b/server/src/com/vaadin/ui/Component.java index 85140a8351..afde1cdbdf 100644 --- a/server/src/com/vaadin/ui/Component.java +++ b/server/src/com/vaadin/ui/Component.java @@ -18,7 +18,6 @@ package com.vaadin.ui; import java.io.Serializable; import java.util.EventListener; -import java.util.EventObject; import java.util.Locale; import com.vaadin.event.FieldEvents; @@ -580,11 +579,6 @@ public interface Component extends ClientConnector, Sizeable, Serializable { * application, the {@code attach()} is called immediately from * {@link #setParent(Component)}. * </p> - * <p> - * This method must call {@link UI#componentAttached(Component)} to let the - * UI know that a new Component has been attached. - * </p> - * * * <pre> * public class AttachExample extends CustomComponent { @@ -735,7 +729,7 @@ public interface Component extends ClientConnector, Sizeable, Serializable { * @see Component.Listener */ @SuppressWarnings("serial") - public static class Event extends EventObject { + public static class Event extends ConnectorEvent { /** * Constructs a new event with the specified source component. diff --git a/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java b/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java new file mode 100644 index 0000000000..f5b940d97b --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java @@ -0,0 +1,180 @@ +/* +@VaadinApache2LicenseForJavaFiles@ + */ + +package com.vaadin.tests.server.clientconnector; + +import org.easymock.EasyMock; +import org.easymock.IArgumentMatcher; +import org.easymock.IMocksControl; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.server.ClientConnector.AttachEvent; +import com.vaadin.server.ClientConnector.AttachListener; +import com.vaadin.server.ClientConnector.ConnectorEvent; +import com.vaadin.server.ClientConnector.DetachEvent; +import com.vaadin.server.ClientConnector.DetachListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.UI; + +public class AttachDetachListeners { + + private IMocksControl control; + + private VaadinSession session; + private UI ui; + private Layout content; + private Component component; + + AttachListener attachListener; + DetachListener detachListener; + + @Before + public void setUp() { + control = EasyMock.createStrictControl(); + + session = new VaadinSession(control.createMock(VaadinService.class)); + + ui = new UI() { + @Override + protected void init(VaadinRequest request) { + } + }; + content = new CssLayout(); + component = new Label(); + + attachListener = control.createMock(AttachListener.class); + detachListener = control.createMock(DetachListener.class); + } + + @Test + public void attachListeners_setSessionLast() { + setupAttachListeners(); + + ui.setContent(content); + content.addComponent(component); + ui.setSession(session); + + control.verify(); + } + + @Test + public void attachListeners_setSessionFirst() { + setupAttachListeners(); + + ui.setSession(session); + ui.setContent(content); + content.addComponent(component); + + control.verify(); + } + + @Test + public void attachListeners_setSessionBetween() { + setupAttachListeners(); + + ui.setContent(content); + ui.setSession(session); + content.addComponent(component); + + control.verify(); + } + + @Test + public void detachListeners_setSessionNull() { + setupDetachListeners(); + + ui.setContent(content); + content.addComponent(component); + ui.setSession(null); + + control.verify(); + } + + @Test + public void detachListeners_removeComponent() { + setupDetachListeners(); + + ui.setContent(content); + content.addComponent(component); + content.removeAllComponents(); + ui.setSession(null); + + control.verify(); + } + + @Test + public void detachListeners_setContentNull() { + setupDetachListeners(); + + ui.setContent(content); + content.addComponent(component); + ui.setContent(null); + ui.setSession(null); + + control.verify(); + } + + public static class EventEquals<E extends ConnectorEvent> implements + IArgumentMatcher { + + private E expected; + + public EventEquals(E expected) { + this.expected = expected; + } + + @Override + public void appendTo(StringBuffer buffer) { + buffer.append("EventEquals("); + buffer.append("expected " + expected.getClass().getSimpleName() + + " with connector " + expected.getConnector()); + buffer.append(")"); + } + + @Override + public boolean matches(Object argument) { + return expected.getClass().isInstance(argument) + && ((ConnectorEvent) argument).getConnector() == expected + .getConnector(); + } + } + + public static <E extends ConnectorEvent> E eventEquals(E expected) { + EasyMock.reportMatcher(new EventEquals<E>(expected)); + return null; + } + + private void setupDetachListeners() { + detachListener.detach(eventEquals(new DetachEvent(component))); + detachListener.detach(eventEquals(new DetachEvent(content))); + detachListener.detach(eventEquals(new DetachEvent(ui))); + + control.replay(); + + ui.addDetachListener(detachListener); + content.addDetachListener(detachListener); + component.addDetachListener(detachListener); + + ui.setSession(session); + } + + private void setupAttachListeners() { + attachListener.attach(eventEquals(new AttachEvent(ui))); + attachListener.attach(eventEquals(new AttachEvent(content))); + attachListener.attach(eventEquals(new AttachEvent(component))); + + control.replay(); + + ui.addAttachListener(attachListener); + content.addAttachListener(attachListener); + component.addAttachListener(attachListener); + } +} |