Repository: peholmst/vaadin4spring Branch: master Commit: 8184d775454c Files: 32 Total size: 120.0 KB Directory structure: gitextract_rbhuxx4x/ ├── .gitignore ├── LICENSE.txt ├── README.md ├── eventbus/ │ ├── README.md │ ├── pom.xml │ └── src/ │ ├── main/ │ │ └── java/ │ │ └── org/ │ │ └── vaadin/ │ │ └── spring/ │ │ └── events/ │ │ ├── Event.java │ │ ├── EventBus.java │ │ ├── EventBusAware.java │ │ ├── EventBusListener.java │ │ ├── EventBusListenerMethodFilter.java │ │ ├── EventScope.java │ │ ├── ExactTopicFilter.java │ │ ├── HierachyTopicFilter.java │ │ ├── NoEventBusListenerMethodFilter.java │ │ ├── TopicFilter.java │ │ ├── annotation/ │ │ │ ├── EnableEventBus.java │ │ │ ├── EventBusListenerMethod.java │ │ │ ├── EventBusListenerTopic.java │ │ │ └── EventBusProxy.java │ │ ├── config/ │ │ │ └── EventBusConfiguration.java │ │ ├── internal/ │ │ │ ├── AbstractListenerWrapper.java │ │ │ ├── ClassUtils.java │ │ │ ├── EventBusListenerWrapper.java │ │ │ ├── ListenerCollection.java │ │ │ ├── MethodListenerWrapper.java │ │ │ └── ScopedEventBus.java │ │ └── support/ │ │ ├── ApplicationContextEventBroker.java │ │ └── VaadinEventBusAwareProcessor.java │ └── test/ │ └── java/ │ └── org/ │ └── vaadin/ │ └── spring/ │ └── events/ │ ├── integration/ │ │ └── ScopedEventBusIntegrationTest.java │ ├── internal/ │ │ └── ScopedEventBusTest.java │ └── support/ │ └── ApplicationContextEventBrokerTest.java └── pom.xml ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.orig # Maven target/ *.releaseBackup release.properties # IntelliJ *.iml .idea/ # Eclipse .settings/ *.project *.classpath *.springBeans # Vaadin samples/*-sample*/src/main/webapp/VAADIN/themes/*.css samples/*-sample*/src/main/webapp/VAADIN/themes/**/*.css samples/*-sample*/src/main/webapp/VAADIN/widgetsets/ samples/*-sample*/src/main/resources/VAADIN/widgetsets/ samples/*-sample*/src/main/resources/VAADIN/themes/**/*.css # JRebel rebel.xml ================================================ FILE: LICENSE.txt ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] 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. ================================================ FILE: README.md ================================================ Vaadin4Spring ============= This project started as a prototype of an official Vaadin Spring add-on, back in the day when Vaadin 8 was new. That was many years ago and a lot has happened since that time. Now we have Vaadin 14 and I have deleted almost all of the extensions and addons since it did not make sense to port them to Vaadin 14. All that remains is the event bus that is kept by popular demand. **Please note, however, that the event bus has a design flaw that can lead to potentially serious security problems if used in the wrong way.** Check the [README](eventbus/README.md) for more information. ================================================ FILE: eventbus/README.md ================================================ The Vaadin4Spring Event Bus =========================== **Do not use this event bus in new projects.** This version is published to make it easier to port existing applications that use Vaadin4Spring from Vaadin 8 to Vaadin 14. ## Security Issue with the Application Scoped Event Bus You can run into serious security problems with the application scoped event bus if you are not careful. Events are dispatched synchronously to all listeners in the thread that originally fired the event. This means that **all event listeners will run within the security context of the user that fired the event**. Because of this I seriously discourage developers from using the application scoped event bus in their projects. I can't fix this issue either since that would break existing software that relies on the events being dispatched in this way. There are discussions of creating a completely new event bus for Vaadin 14+ that would not have this design flaw. ================================================ FILE: eventbus/pom.xml ================================================ 4.0.0 org.vaadin.spring parent-pom 14.0.1-SNAPSHOT ../ org.vaadin.spring.addons vaadin-spring-addon-eventbus jar Vaadin4Spring Event Bus com.vaadin vaadin-spring org.mockito mockito-core test org.junit.jupiter junit-jupiter-engine test org.springframework spring-test test com.vaadin vaadin-spring-boot-starter test ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/Event.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; import java.io.Serializable; /** * A class that represents an event that has been published on an {@link org.vaadin.spring.events.EventBus}. * * @author Petter Holmström (petter@vaadin.com) * @see EventBus#publish(Object, Object) */ public class Event implements Serializable { private static final long serialVersionUID = 4818820872533486223L; private final EventBus eventBus; private final Object source; private final String topic; private final long timestamp; private final T payload; public Event(EventBus eventBus, Object source, T payload) { this(eventBus, source, payload, ""); } public Event(EventBus eventBus, Object source, T payload, String topic) { this.eventBus = eventBus; this.source = source; this.payload = payload; this.topic = topic != null ? topic : ""; this.timestamp = System.currentTimeMillis(); } /** * Gets the event bus on which the event was originally published. * * @return the event bus, never {@code null}. */ public EventBus getEventBus() { return eventBus; } /** * Gets the scope of the event. * * @return the scope, never {@code null}. */ public EventScope getScope() { return eventBus.getScope(); } /** * Gets the object that published the event on the event bus. * * @return the source of the event, never {@code null}. */ public Object getSource() { return source; } /** * Gets the string which specifies the topic of the event on the event bus. * * @return the topic of the event, never {@code null}. */ public String getTopic() { return topic; } /** * Gets the timestamp when the event was published on the event bus. * * @return the timestamp. */ public long getTimestamp() { return timestamp; } /** * Gets the payload of the event. * * @return the payload, never {@code null}. */ public T getPayload() { return payload; } @Override public String toString() { return String.format("%s[scope=%s, eventBus=%s, ts=%d, source=[%s], payload=[%s]]", getClass().getSimpleName(), getScope(), getEventBus(), getTimestamp(), getSource(), getPayload()); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/EventBus.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** * Interface defining an event bus. This event bus infrastructure complements the * {@link org.springframework.context.ApplicationEventPublisher} in the following ways: * *

* There are three event scopes, and therefore three event bus types (each with their own sub interface): *

    *
  1. {@link EventScope#APPLICATION} events are published to the entire application.
  2. *
  3. {@link EventScope#SESSION} events are published to the current session.
  4. *
  5. {@link EventScope#UI} events are published to the current UI.
  6. *
*

* The event buses are chained in the following way: *

* Furthermore, {@link org.springframework.context.ApplicationEventPublisher} events can be propagated to any event bus * by using {@link org.vaadin.spring.events.support.ApplicationContextEventBroker}. *

* You select which {@code EventBus} implementation to inject by using the corresponding interface. For example, to * inject the UI-scoped event bus, you would use: * * @Autowired UIEventBus myUIScopedEventBus; * * With this implementation, you can subscribe to and publish events of the application, session and UI scopes (see * {@link #publish(EventScope, Object, Object)}). * * @author Petter Holmström (petter@vaadin.com) */ public interface EventBus { /** * Publishes the specified payload on the event bus, using the scope of this particular event bus. * * @param sender the object that published the event, never {@code null}. * @param payload the payload of the event to publish, never {@code null}. * @param the type of the payload. * @see #getScope() */ void publish(Object sender, T payload); /** * Publishes the specified payload on the event bus, using the scope of this particular event bus. * The topic specifies which listeners will be notified. * * @param topic the topic of the event to publish, never {@code null}. * @param sender the object that published the event, never {@code null}. * @param payload the payload of the event to publish, never {@code null}. * @param the type of the payload. * @see #getScope() */ void publish(String topic, Object sender, T payload); /** * Publishes the specified payload on the event bus, or any of its parent buses, depending on the event scope. * * @param scope the scope of the event, never {@code null}. * @param sender the object that published the event, never {@code null}. * @param payload the payload of the event to publish, never {@code null}. * @param the type of the payload; * @throws UnsupportedOperationException if the payload could not be published with the specified scope. * @see #publish(Object, Object) */ void publish(EventScope scope, Object sender, T payload) throws UnsupportedOperationException; /** * Publishes the specified payload on the event bus, or any of its parent buses, depending on the event scope. * The topic specifies which listeners will be notified. * * @param scope the scope of the event, never {@code null}. * @param topic the topic of the event to publish, never {@code null}. * @param sender the object that published the event, never {@code null}. * @param payload the payload of the event to publish, never {@code null}. * @param the type of the payload; * @throws UnsupportedOperationException if the payload could not be published with the specified scope. * @see #publish(Object, Object) */ void publish(EventScope scope, String topic, Object sender, T payload) throws UnsupportedOperationException; /** * Gets the scope of the events published on this event bus. * * @return the event scope, never {@code null}. * @see org.vaadin.spring.events.Event#getScope() */ EventScope getScope(); /** * Subscribes the specified listener to the event bus, including propagated events from parent event buses. * The event bus will analyse the payload type of the listener to determine which events it is interested in * receiving. * This is the same as calling {@link #subscribe(EventBusListener, boolean) subscribe(listener, true)}. * * @param listener the listener to subscribe, never {@code null}. * @param the type of payload the listener is interested in. * @see #unsubscribe(EventBusListener) * @see #subscribeWithWeakReference(EventBusListener) */ void subscribe(EventBusListener listener); /** * Same as {@link #subscribe(EventBusListener)}, but uses a weak reference to store the listener internally. */ void subscribeWithWeakReference(EventBusListener listener); /** * Subscribes the topic interested listener to the event bus, including propagated events from parent event buses. * The event bus will analyse the payload type and topic of the listener to determine which events it is interested * in receiving. * @param listener the listener to subscribe, never {@code null} * @param topic the topic of listener interest * * @param the type of payload the listener is interested in */ void subscribe(EventBusListener listener, String topic); /** * Same as {@link #subscribe(EventBusListener, String)}, but uses a weak reference to store the listener * internally. */ void subscribeWithWeakReference(EventBusListener listener, String topic); /** * Subscribes the specified listener to the event bus. The event bus will analyse the * payload type of the listener to determine which events it is interested in receiving. * * @param listener the listener to subscribe, never {@code null}. * @param includingPropagatingEvents true to notify the listener of events that have propagated from the chain of * parent event buses, false to only notify the listeners of events that are directly published on this event * bus. * @param the type of payload the listener is interested in. * @see #unsubscribe(EventBusListener) * @see #subscribeWithWeakReference(EventBusListener, boolean) */ void subscribe(EventBusListener listener, boolean includingPropagatingEvents); /** * Same as {@link #subscribe(EventBusListener, boolean)}, but uses a weak reference to store the listener * internally. */ void subscribeWithWeakReference(EventBusListener listener, boolean includingPropagatingEvents); /** * Subscribes the specified listener to the event bus. The listener need not implement the * {@link org.vaadin.spring.events.EventBusListener} interface, * but must contain one or more methods that are annotated with the * {@link org.vaadin.spring.events.annotation.EventBusListenerMethod} interface and conform to one of these method * signatures: myMethodName(Event<MyPayloadType>) or myMethodName(MyPayloadType). * The event bus will analyse the payload type of the listener methods to determine * which events the different methods are interested in receiving. This is the same as calling * {@link #subscribe(Object, boolean) subscribe(listener, true)}. * * @param listener the listener to subscribe, never {@code null}. * @see #subscribeWithWeakReference(Object) */ void subscribe(Object listener); /** * Same as {@link #subscribe(Object)}, but uses a weak reference to store the listener internally. */ void subscribeWithWeakReference(Object listener); /** * Subscribes the topic interested listener to the event bus. The listener need not implement the * {@link org.vaadin.spring.events.EventBusListener} interface, * but must contain one or more methods that are annotated with the * {@link org.vaadin.spring.events.annotation.EventBusListenerMethod} interface and conform to one of these method * signatures: myMethodName(Event<MyPayloadType>) or myMethodName(MyPayloadType). * The event bus will analyse the payload type of the listener methods to determine * which events the different methods are interested in receiving. *

* Example: * subscribe(Listener.this, "/news"); * @param listener the listener to subscribe, never {@code null}. * @param topic the topic of listener interest * @see #subscribeWithWeakReference(Object, String) */ void subscribe(Object listener, String topic); /** * Same as {@link #subscribe(Object, String)}, but uses a weak reference to store the listener internally. */ void subscribeWithWeakReference(Object listener, String topic); /** * Subscribes the specified listener to the event bus. The listener need not implement the * {@link org.vaadin.spring.events.EventBusListener} interface, * but must contain one or more methods that are annotated with the * {@link org.vaadin.spring.events.annotation.EventBusListenerMethod} interface and conform to one of these method * signatures: myMethodName(Event<MyPayloadType>) or myMethodName(MyPayloadType). * The event bus will analyse the payload type of the listener methods to determine * which events the different methods are interested in receiving. * * @param listener the listener to subscribe, never {@code null}. * @param includingPropagatingEvents true to notify the listener of events that have propagated from the chain of * parent event buses, false to only notify the listeners of events that are directly published on this event * bus. * @see #unsubscribe(Object) * @see #subscribeWithWeakReference(Object, boolean) */ void subscribe(Object listener, boolean includingPropagatingEvents); /** * Same as {@link #subscribe(Object, boolean)}, but uses a weak reference to store the listener internally. */ void subscribeWithWeakReference(Object listener, boolean includingPropagatingEvents); /** * Unsubscribes the specified listener from the event bus. Also works for listeners stored with weak references. * * @param listener the listener to unsubscribe, never {@code null}. * @param the type of the payload. * @see #subscribe(EventBusListener) * @see #subscribe(EventBusListener, boolean) */ void unsubscribe(EventBusListener listener); /** * Unsubscribes the specified listener (and all its listener methods) from the event bus. Also works for listeners * stored with weak references. * * @param listener the listener to unsubscribe, never {@code null}. * @see #subscribe(Object) * @see #subscribe(Object, boolean) */ void unsubscribe(Object listener); /** * Interface implemented by the application scoped event bus. * * @see org.vaadin.spring.events.EventScope#APPLICATION */ interface ApplicationEventBus extends EventBus { } /** * Interface implemented by the session scoped event bus. * * @see org.vaadin.spring.events.EventScope#SESSION */ interface SessionEventBus extends EventBus { } /** * Interface implemented by the UI scoped event bus. * * @see org.vaadin.spring.events.EventScope#UI */ interface UIEventBus extends EventBus { } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/EventBusAware.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; import org.springframework.beans.factory.Aware; /** * Marker super interface for beans that want to get notified of a specific type of event bus. * * @author Petter Holmström (petter@vaadin.com) */ public interface EventBusAware extends Aware { /** * Interface to be implemented by beans that want to get notified of the * application event bus. */ interface ApplicationEventBusAware extends EventBusAware { /** * Sets the application scoped event bus. */ void setApplicationEventBus(EventBus.ApplicationEventBus applicationEventBus); } /** * Interface to be implemented by beans that want to get notified of the * session event bus. */ interface SessionEventBusAware extends EventBusAware { /** * Sets the session scoped event bus. */ void setSessionEventBus(EventBus.SessionEventBus sessionEventBus); } /** * Interface to be implemented by beans that want to get notified of the * UI event bus. */ interface UIEventBusAware extends EventBusAware { /** * Sets the UI scoped event bus. */ void setUIEventBus(EventBus.UIEventBus uiEventBus); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/EventBusListener.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; import java.io.Serializable; /** * Interface to be implemented by listeners that want to subscribe to an {@link org.vaadin.spring.events.EventBus}. * * @param type of event payloads that the listener is interested in receiving. * @author Petter Holmström (petter@vaadin.com) */ public interface EventBusListener extends Serializable { /** * Called when an event has been received. * * @param event the event, never {@code null}. */ void onEvent(Event event); } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/EventBusListenerMethodFilter.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** *

* A method annotated with @EventBusListenerMethod will be invoked if it is * subscribed to the EventBus that published the Event * (or Object payload). That method will oftentimes employ filtering code * in the method internals, because of the possibility that multiple annotated methods * might listen to same the Event. *

*

* As a convenience, an implementation of this filter may be defined in * {@link org.vaadin.spring.events.annotation.EventBusListenerMethod#filter()} * and stand in place of such filtering code. *

* @author Chris Phillipson (fastnsilver@gmail.com) * */ public interface EventBusListenerMethodFilter { /** * Criteria used to influence when an @EventBusListenerMethod * annotated method with this filter defined will execute * @param event EventBus event * @return true if filtering condition met; false otherwise */ boolean filter(Event event); } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/EventScope.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** * Enumeration of event scopes. * * @author Petter Holmström (petter@vaadin.com) */ public enum EventScope { /** * The event is application wide. */ APPLICATION, /** * The event is specific to the current (Vaadin) session. */ SESSION, /** * The event is specific to the current UI. */ UI, /** * Undefined event scope. An internal event scope used only when no scope has been explicitly defined. * * @see org.vaadin.spring.events.annotation.EventBusListenerMethod#scope() */ UNDEFINED } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/ExactTopicFilter.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** * An implementation of {@link org.vaadin.spring.events.TopicFilter} * which validates the topics with an exact match (equals). * * @author Marco Luthardt (marco.luthardt@iandme.net) */ public class ExactTopicFilter implements TopicFilter { @Override public boolean validTopic(String eventTopic, String listenerTopic) { return eventTopic.equals(listenerTopic); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/HierachyTopicFilter.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** * An implementation of {@link org.vaadin.spring.events.TopicFilter} * which validates the topics hierarchical. This means, that the * listener filter will be checked as prefixed substring against * the event topic. * *
    *
  1. match: eventTopic = "foo.bar" and listenerTopic = "foo"
  2. *
  3. no match: eventTopic = "foo" and listenerTopic = "foo.bar"
  4. *
  5. no match: eventTopic = "foo.bar" and listenerTopic = "foo.not"
  6. *
* * @author Marco Luthardt (marco.luthardt@iandme.net) */ public class HierachyTopicFilter implements TopicFilter { @Override public boolean validTopic(String eventTopic, String listenerTopic) { return eventTopic.startsWith(listenerTopic); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/NoEventBusListenerMethodFilter.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** * A default filter implementation which always returns true. * @author Chris Phillipson (fastnsilver@gmail.com) * @see org.vaadin.spring.events.annotation.EventBusListenerMethod#scope() */ public class NoEventBusListenerMethodFilter implements EventBusListenerMethodFilter { @Override public boolean filter(Event event) { return true; } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/TopicFilter.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events; /** * The interface defines a method to validate a given event topic * an listener topic. The event topic is provided when publishing * an event and the listener topic by the {@link org.vaadin.spring.events.annotation.EventBusListenerTopic} * annotation. An implementation of this interface can be used as * parameter of this annotation. * * @author Marco Luthardt (marco.luthardt@iandme.net) * @see org.vaadin.spring.events.annotation.EventBusListenerTopic */ public interface TopicFilter { /** * Validates the given event topic against the listener topic. * * @param eventTopic the topic provided by while publishing an event, never {@code null} * @param listenerTopic the topic of the listener method, never {@code null} * * @return true true if the event topic matches the listener topic, otherwise false */ boolean validTopic(String eventTopic, String listenerTopic); } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/annotation/EnableEventBus.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.context.annotation.Import; import org.vaadin.spring.events.config.EventBusConfiguration; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EventBusConfiguration.class) public @interface EnableEventBus { } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/annotation/EventBusListenerMethod.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBusListener; import org.vaadin.spring.events.EventBusListenerMethodFilter; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.NoEventBusListenerMethodFilter; /** * Annotation to be placed on event bus listener methods. A listener method must always conform to one of the following method signatures: *
    *
  1. myMethodName({@link org.vaadin.spring.events.Event Event}<MyPayloadType>)
  2. *
  3. myMethodName(MyPayloadType)
  4. *
* A listener method can have any visibility and any return type. * * @author Petter Holmström (petter@vaadin.com) * @see org.vaadin.spring.events.EventBusListener * @see EventBus#subscribe(Object) * @see EventBus#subscribe(Object, boolean) */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EventBusListenerMethod { /** * The default scope of a listener method is EventScope.UNDEFINED * This means that listener will listen for any {@link EventScope} if {@link EventBus#subscribe(EventBusListener, boolean)} is set to propagate event */ EventScope scope() default EventScope.UNDEFINED; Class filter() default NoEventBusListenerMethodFilter.class; /** * Filter by source class */ Class[] source() default {}; } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/annotation/EventBusListenerTopic.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.vaadin.spring.events.ExactTopicFilter; import org.vaadin.spring.events.TopicFilter; /** * Annotation to be placed on event bus listener methods, additional to * the {@link org.vaadin.spring.events.annotation.EventBusListenerMethod} annotation. * A topic is specified as string which will be defined when publishing an event. * Each method annotated with this annotation and the corresponding topic will be * called as listener. * * Topics can be filtered with implementations of the {@link org.vaadin.spring.events.TopicFilter} interface. * * @author Marco Luthardt (marco.luthardt@iandme.net) * @see org.vaadin.spring.events.annotation.EventBusListenerMethod * @see org.vaadin.spring.events.TopicFilter */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface EventBusListenerTopic { /** * A topic is a string which can be specified while publishing an event. The * method will only called when the topic matches the given String in the * published method. */ String topic() default ""; /** * The filter to be used to validate the published and listener topic. * * @return an implementation of the {@link org.vaadin.spring.events.TopicFilter} interface. */ Class filter() default ExactTopicFilter.class; } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/annotation/EventBusProxy.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.annotation; import org.springframework.beans.factory.annotation.Qualifier; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Qualifier to be used to indicate that a proxy of an {@link org.vaadin.spring.events.EventBus} should be injected. * * @author Petter Holmström (petter@vaadin.com) */ @Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented @Qualifier public @interface EventBusProxy { } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/config/EventBusConfiguration.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.config; import com.vaadin.flow.spring.scopes.VaadinSessionScope; import com.vaadin.flow.spring.scopes.VaadinUIScope; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; import org.springframework.context.annotation.Scope; import org.springframework.context.annotation.ScopedProxyMode; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.annotation.EventBusProxy; import org.vaadin.spring.events.internal.ScopedEventBus; import org.vaadin.spring.events.support.VaadinEventBusAwareProcessor; /** * Configuration class to configure the Spring Vaadin Eventbus * * @author Gert-Jan Timmer (gjr.timmer@gmail.com) */ @Configuration public class EventBusConfiguration { @Bean VaadinEventBusAwareProcessor vaadinEventBusProcessor() { return new VaadinEventBusAwareProcessor(); } @Bean EventBus.ApplicationEventBus applicationEventBus() { return new ScopedEventBus.DefaultApplicationEventBus(); } @Bean @Scope(value = VaadinSessionScope.VAADIN_SESSION_SCOPE_NAME, proxyMode = ScopedProxyMode.INTERFACES) @EventBusProxy EventBus.SessionEventBus proxiedSessionEventBus() { return sessionEventBus(); } @Bean @Scope(value = VaadinSessionScope.VAADIN_SESSION_SCOPE_NAME, proxyMode = ScopedProxyMode.NO) @Primary EventBus.SessionEventBus sessionEventBus() { return new ScopedEventBus.DefaultSessionEventBus(applicationEventBus()); } @Bean @Scope(value = VaadinUIScope.VAADIN_UI_SCOPE_NAME, proxyMode = ScopedProxyMode.INTERFACES) @EventBusProxy EventBus.UIEventBus proxiedUiEventBus() { return uiEventBus(); } @Bean @Scope(value = VaadinUIScope.VAADIN_UI_SCOPE_NAME, proxyMode = ScopedProxyMode.NO) @Primary EventBus.UIEventBus uiEventBus() { return new ScopedEventBus.DefaultUIEventBus(sessionEventBus()); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/internal/AbstractListenerWrapper.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; import org.vaadin.spring.events.Event; import org.vaadin.spring.events.EventBus; /** * Base implementation of {@link org.vaadin.spring.events.internal.ListenerCollection.Listener} that implements * the {@link #supports(org.vaadin.spring.events.Event)} method. An event is supported if: * * * @author Petter Holmström (petter@vaadin.com) */ abstract class AbstractListenerWrapper implements ListenerCollection.Listener { private static final long serialVersionUID = 6211420845165980671L; private final EventBus owningEventBus; private final Object listenerTarget; private final boolean includingPropagatingEvents; private final String topic; AbstractListenerWrapper(EventBus owningEventBus, Object listenerTarget, String topic, boolean includingPropagatingEvents) { this.owningEventBus = owningEventBus; this.topic = topic; this.listenerTarget = listenerTarget; this.includingPropagatingEvents = includingPropagatingEvents; } /** * Gets the payload type of the listener. */ protected abstract Class getPayloadType(); /** * Gets the target object that this listener is wrapping. */ public Object getListenerTarget() { return listenerTarget; } @Override public boolean supports(Event event) { final Class eventPayloadType = event.getPayload().getClass(); return (event.getTopic().equals(topic) || topic == null) && getPayloadType().isAssignableFrom(eventPayloadType) && (includingPropagatingEvents || event.getEventBus().equals(owningEventBus)); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/internal/ClassUtils.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; /** * Utility methods for working with classes and class members. * * @author Petter Holmström (petter@vaadin.com) */ final class ClassUtils { private ClassUtils() { } /** * Visitor interface used with {@link ClassUtils#visitClassHierarchy(ClassUtils.ClassVisitor, Class)}. */ public interface ClassVisitor { void visit(Class clazz); } /** * Visits the entire class hierarchy from {@code subClass} to {@code Object}. * * @param visitor the visitor to use, must not be {@code null}. * @param subClass the class whose hierarchy is to be visited, must not be {@code null}. */ static void visitClassHierarchy(ClassVisitor visitor, Class subClass) { Class visitedClass = subClass; while (visitedClass.getSuperclass() != null) { visitor.visit(visitedClass); visitedClass = visitedClass.getSuperclass(); } } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/internal/EventBusListenerWrapper.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; import org.springframework.core.GenericTypeResolver; import org.springframework.util.Assert; import org.vaadin.spring.events.Event; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBusListener; /** * Implementation of {@link org.vaadin.spring.events.internal.AbstractListenerWrapper} that wraps a single * {@link org.vaadin.spring.events.EventBusListener} instance. * * @author Petter Holmström (petter@vaadin.com) */ class EventBusListenerWrapper extends AbstractListenerWrapper { private static final long serialVersionUID = 8964309195124823892L; private final Class payloadType; EventBusListenerWrapper(EventBus owningEventBus, EventBusListener listenerTarget, String topic, boolean includingPropagatingEvents) { super(owningEventBus, listenerTarget, topic, includingPropagatingEvents); payloadType = GenericTypeResolver.resolveTypeArgument(listenerTarget.getClass(), EventBusListener.class); Assert.notNull(payloadType, "Could not resolve payload type"); } @Override @SuppressWarnings("rawtypes") public EventBusListener getListenerTarget() { return (EventBusListener) super.getListenerTarget(); } @Override protected Class getPayloadType() { return payloadType; } @Override @SuppressWarnings("unchecked") public void publish(Event event) { getListenerTarget().onEvent(event); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/internal/ListenerCollection.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; import java.io.Serializable; import java.util.*; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.vaadin.spring.events.Event; /** * A collection of listeners. Intended only for internal use by the framework. * * @author Petter Holmström (petter@vaadin.com) */ class ListenerCollection implements Serializable { private static final long serialVersionUID = -6237902400879667320L; private final Logger logger = LoggerFactory.getLogger(getClass()); private final Set listeners = new HashSet(); private final Set weakListeners = Collections.newSetFromMap(new WeakHashMap()); /** * Interface defining a listener. */ public interface Listener extends Serializable { /** * Checks if this listener supports the specified event. * * @param event the event to check, never {@code null}. * @return true if the event is supported, false otherwise. */ boolean supports(Event event); /** * Publishes the event to the listener. * * @param event the event to publish, never {@code null}. */ void publish(Event event); } /** * Interface defining a listener filter. */ public interface ListenerFilter { /** * Checks if the specified listener passes the filter. * * @param listener the listener to check, never {@code null}. * @return true if the listener passes the filter, false otherwise. */ boolean passes(Listener listener); } /** * Adds the specified {@link org.vaadin.spring.events.internal.ListenerCollection.Listener} to the listener * collection. * * @param listener the listener to add, never {@code null}. * @see #remove(org.vaadin.spring.events.internal.ListenerCollection.Listener) */ void add(Listener listener) { logger.trace("Adding listener [{}]", listener); synchronized (listeners) { listeners.add(listener); } } /** * Adds the specified {@link org.vaadin.spring.events.internal.ListenerCollection.Listener} to the listener * collection, * using a weak reference. This means the listener does not need to be removed to be eligible for garbage * collection. * * @param listener the listener to add, never {@code null}. */ void addWithWeakReference(Listener listener) { logger.trace("Adding listener [{}] using a weak reference", listener); synchronized (weakListeners) { weakListeners.add(listener); } } /** * Removes a {@link org.vaadin.spring.events.internal.ListenerCollection.Listener} previously added by  * {@link #add(org.vaadin.spring.events.internal.ListenerCollection.Listener)}. * If no listener definition is found in the collection, nothing happens. * * @param listener the listener to remove, never {@code null}. * @see #add(org.vaadin.spring.events.internal.ListenerCollection.Listener) */ void remove(Listener listener) { logger.trace("Removing listener [{}]", listener); synchronized (listeners) { listeners.remove(listener); } synchronized (weakListeners) { weakListeners.remove(listener); } } /** * Removes all {@link org.vaadin.spring.events.internal.ListenerCollection.Listener}s that pass the specified filter * and that were previously added by * {@link #add(org.vaadin.spring.events.internal.ListenerCollection.Listener)}. * * @param filter the filter that specifies which listeners to remove, never {@code null}. */ void removeAll(ListenerFilter filter) { synchronized (listeners) { removeFilteredListenersFromSet(filter, listeners); } synchronized (weakListeners) { removeFilteredListenersFromSet(filter, weakListeners); } } /** * Removes all {@link org.vaadin.spring.events.internal.ListenerCollection.Listener}s from the collection. */ void clear() { synchronized (listeners) { listeners.clear(); } synchronized (weakListeners) { weakListeners.clear(); } } /** * Publishes the specified {@code event} to all * {@link org.vaadin.spring.events.internal.ListenerCollection.Listener}s that support it. * * @param event the event to publish, never {@code null}. * @see org.vaadin.spring.events.internal.ListenerCollection.Listener#publish(org.vaadin.spring.events.Event) * @see org.vaadin.spring.events.internal.ListenerCollection.Listener#supports(org.vaadin.spring.events.Event) */ public void publish(Event event) { Set interestedListeners = new HashSet(); synchronized (listeners) { addSupportedListenersToSet(listeners, interestedListeners, event); } synchronized (weakListeners) { addSupportedListenersToSet(weakListeners, interestedListeners, event); } if (interestedListeners.isEmpty()) { logger.debug("No listeners supported event [{}]", event); } else { for (Listener listener : interestedListeners) { logger.trace("Publishing event [{}] to listener [{}]", event, listener); listener.publish(event); } } } private void addSupportedListenersToSet(Set candidateListeners, Set selectedListeners, Event event) { for (Listener candidateListener : candidateListeners) { if (candidateListener.supports(event)) { logger.trace("Listener [{}] supports event [{}]", candidateListener, event); selectedListeners.add(candidateListener); } } } private void removeFilteredListenersFromSet(ListenerFilter filter, Set listenerSet) { for (Iterator it = listenerSet.iterator(); it.hasNext();) { Listener listener = it.next(); if (filter.passes(listener)) { logger.trace("Removing listener [{}]", listener); it.remove(); } } } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/internal/MethodListenerWrapper.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; import org.vaadin.spring.events.*; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import org.vaadin.spring.events.annotation.EventBusListenerTopic; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; /** * Implementation of {@link org.vaadin.spring.events.internal.AbstractListenerWrapper} that wraps an object * that contains a method annotated with {@link org.vaadin.spring.events.annotation.EventBusListenerMethod}. If the object * contains multiple listener methods, multiple instances of this class should be created. * * @author Petter Holmström (petter@vaadin.com) */ class MethodListenerWrapper extends AbstractListenerWrapper { private static final long serialVersionUID = -3624543380547361337L; private final Class payloadType; private final boolean payloadMethod; private transient Method listenerMethod; private final String topic; MethodListenerWrapper(EventBus owningEventBus, Object listenerTarget, String topic, boolean includingPropagatingEvents, Method listenerMethod) { super(owningEventBus, listenerTarget, topic, includingPropagatingEvents); this.topic = topic; if (listenerMethod.getParameterTypes()[0] == Event.class) { ParameterizedType type = (ParameterizedType) listenerMethod.getGenericParameterTypes()[0]; payloadType = (Class) type.getActualTypeArguments()[0]; payloadMethod = false; } else { payloadType = listenerMethod.getParameterTypes()[0]; payloadMethod = true; } this.listenerMethod = listenerMethod; } private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { ois.defaultReadObject(); // TODO Read listener method info and look up method } private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); // TODO Write listener method info } @Override public Class getPayloadType() { return payloadType; } @Override public void publish(Event event) { listenerMethod.setAccessible(true); try { if (payloadMethod) { listenerMethod.invoke(getListenerTarget(), event.getPayload()); } else { listenerMethod.invoke(getListenerTarget(), event); } } catch (IllegalAccessException e) { throw new RuntimeException("Could not access listener method " + listenerMethod.getName()); } catch (InvocationTargetException e) { Throwable targetException = e.getTargetException(); if (targetException instanceof RuntimeException) { throw (RuntimeException) targetException; } else { throw new RuntimeException("A checked exception occurred while invoking listener method " + listenerMethod.getName(), targetException); } } } @Override public boolean supports(Event event) { boolean supports = super.supports(event); try { if (listenerMethod.isAnnotationPresent(EventBusListenerMethod.class)) { supports = supports && isInterestedListenerMethod(event); } if (topic != null) { return supports; } if (listenerMethod.isAnnotationPresent(EventBusListenerTopic.class) && supports) { supports = isInTopic(event); } else if (!event.getTopic().isEmpty()) { supports = false; } } catch (Exception e) { throw new RuntimeException("A checked exception occurred while invoking listener method " + listenerMethod.getName(), e); } return supports; } private boolean isInterestedListenerMethod(Event event) throws InstantiationException, IllegalAccessException { EventBusListenerMethod annotation = listenerMethod.getAnnotation(EventBusListenerMethod.class); EventBusListenerMethodFilter filter = annotation.filter().newInstance(); EventScope scope = annotation.scope(); if (scope.equals(EventScope.UNDEFINED)) { scope = event.getScope(); } return filter.filter(event) && event.getScope().equals(scope) && isFromSource(event, annotation.source()); } private boolean isFromSource(Event event, Class[] sources) { if (sources.length == 0) { return true; } boolean result = false; for (int i = 0; i < sources.length && !result; i++) { result |= sources[i].isAssignableFrom(event.getSource().getClass()); } return result; } private boolean isInTopic(Event event) throws InstantiationException, IllegalAccessException { EventBusListenerTopic annotation = listenerMethod.getAnnotation(EventBusListenerTopic.class); TopicFilter filter = annotation.filter().newInstance(); return filter.validTopic(event.getTopic(), annotation.topic()); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/internal/ScopedEventBus.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.aop.framework.Advised; import org.springframework.aop.support.AopUtils; import org.vaadin.spring.events.Event; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBusListener; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import javax.annotation.PreDestroy; import java.io.Serializable; import java.lang.reflect.Method; /** * Implementation of {@link org.vaadin.spring.events.EventBus} that publishes events with one specific * {@link org.vaadin.spring.events.EventScope}. * A scoped event bus can also have a parent event bus, in which case all events published on the parent bus will * propagate to the scoped event bus as well. * * @author Petter Holmström (petter@vaadin.com) */ public abstract class ScopedEventBus implements EventBus, Serializable { private static final long serialVersionUID = -582697574672947883L; private final Logger logger = LoggerFactory.getLogger(getClass()); private final EventScope eventScope; private final ListenerCollection listeners = new ListenerCollection(); private EventBus parentEventBus; private EventBusListener parentListener = new EventBusListener() { private static final long serialVersionUID = -8276470908536582989L; @Override public void onEvent(final Event event) { logger.debug("Propagating event [{}] from parent event bus [{}] to event bus [{}]", event, parentEventBus, ScopedEventBus.this); listeners.publish(event); } }; /** * @param scope the scope of the events that this event bus handles. */ public ScopedEventBus(EventScope scope) { this(scope, null); } /** * @param scope the scope of the events that this event bus handles. * @param parentEventBus the parent event bus to use, may be {@code null}; */ public ScopedEventBus(EventScope scope, EventBus parentEventBus) { eventScope = scope; this.parentEventBus = parentEventBus; if (parentEventBus != null) { if (AopUtils.isJdkDynamicProxy(parentEventBus)) { logger.debug("Parent event bus [{}] is proxied, trying to get the real EventBus instance", parentEventBus); try { this.parentEventBus = (EventBus) ((Advised) parentEventBus).getTargetSource().getTarget(); } catch (Exception e) { logger.error("Could not get target EventBus from proxy", e); throw new RuntimeException("Could not get parent event bus", e); } } logger.debug("Using parent event bus [{}]", this.parentEventBus); this.parentEventBus.subscribe(parentListener); } } @PreDestroy void destroy() { logger.trace("Destroying event bus [{}] and removing all listeners", this); listeners.clear(); if (parentEventBus != null) { parentEventBus.unsubscribe(parentListener); } } @Override public EventScope getScope() { return eventScope; } @Override public void publish(Object sender, T payload) { publish("", sender, payload); } @Override public void publish(String topic, Object sender, T payload) { logger.debug("Publishing payload [{}] from sender [{}] on event bus [{}] in topic [{}]", payload, sender, this, topic); listeners.publish(new Event(this, sender, payload, topic)); } @Override public void publish(EventScope scope, Object sender, T payload) throws UnsupportedOperationException { publish(scope, "", sender, payload); } @Override public void publish(EventScope scope, String topic, Object sender, T payload) throws UnsupportedOperationException { logger.debug("Trying to publish payload [{}] from sender [{}] using scope [{}] on event bus [{}] in topic [{}]", payload, sender, scope, this, topic); if (eventScope.equals(scope)) { publish(topic, sender, payload); } else if (parentEventBus != null) { parentEventBus.publish(scope, topic, sender, payload); } else { logger.warn("Could not publish payload with scope [{}] on event bus [{}]", scope, this); throw new UnsupportedOperationException("Could not publish event with scope " + scope); } } @Override public void subscribe(EventBusListener listener) { subscribe(listener, true); } @Override public void subscribeWithWeakReference(EventBusListener listener) { subscribeWithWeakReference(listener, true); } @Override public void subscribe(EventBusListener listener, String topic) { logger.trace("Subscribing listener [{}] to event bus [{}]", listener, this); listeners.add(new EventBusListenerWrapper(this, listener, topic, true)); } @Override public void subscribe(EventBusListener listener, boolean includingPropagatingEvents) { logger.trace("Subscribing listener [{}] to event bus [{}], includingPropagatingEvents = {}", listener, this, includingPropagatingEvents); listeners.add(new EventBusListenerWrapper(this, listener, null, includingPropagatingEvents)); } @Override public void subscribeWithWeakReference(EventBusListener listener, String topic) { logger.trace("Subscribing listener [{}] to event bus [{}] with weak reference", listener, this); listeners.addWithWeakReference(new EventBusListenerWrapper(this, listener, topic, true)); } @Override public void subscribeWithWeakReference(EventBusListener listener, boolean includingPropagatingEvents) { logger.trace("Subscribing listener [{}] to event bus [{}] with weak reference, includingPropagatingEvents = {}", listener, this, includingPropagatingEvents); listeners.addWithWeakReference(new EventBusListenerWrapper(this, listener, null, includingPropagatingEvents)); } @Override public void subscribe(Object listener) { subscribe(listener, true); } @Override public void subscribe(Object listener, String topic) { subscribe(listener, topic, true, false); } @Override public void subscribeWithWeakReference(Object listener, String topic) { subscribe(listener, topic, true, false); } @Override public void subscribeWithWeakReference(Object listener) { subscribeWithWeakReference(listener, true); } @Override public void subscribe(Object listener, boolean includingPropagatingEvents) { subscribe(listener, null, includingPropagatingEvents, false); } @Override public void subscribeWithWeakReference(Object listener, boolean includingPropagatingEvents) { subscribe(listener, null, includingPropagatingEvents, true); } private void subscribe(final Object listener, final String topic, final boolean includingPropagatingEvents, final boolean weakReference) { logger.trace("Subscribing listener [{}] to event bus [{}], includingPropagatingEvents = {}, weakReference = {}", listener, this, includingPropagatingEvents, weakReference); final int[] foundMethods = new int[1]; ClassUtils.visitClassHierarchy(clazz -> { for (Method m : clazz.getDeclaredMethods()) { if (m.isAnnotationPresent(EventBusListenerMethod.class)) { if (m.getParameterTypes().length == 1) { logger.trace("Found listener method [{}] in listener [{}]", m.getName(), listener); MethodListenerWrapper l = new MethodListenerWrapper(ScopedEventBus.this, listener, topic, includingPropagatingEvents, m); if (weakReference) { listeners.addWithWeakReference(l); } else { listeners.add(l); } foundMethods[0]++; } else { throw new IllegalArgumentException( "Listener method " + m.getName() + " does not have the required signature"); } } } }, listener.getClass()); if (foundMethods[0] == 0) { logger.warn("Listener [{}] did not contain a single listener method!", listener); } } @Override public void unsubscribe(EventBusListener listener) { unsubscribe((Object) listener); } @Override public void unsubscribe(final Object listener) { logger.trace("Unsubscribing listener [{}] from event bus [{}]", listener, this); listeners.removeAll(l -> (l instanceof AbstractListenerWrapper) && (((AbstractListenerWrapper) l).getListenerTarget() == listener)); } /** * Gets the parent of this event bus. Events published on the parent bus will also * propagate to the listeners of this event bus. * * @return the parent event bus, or {@code null} if this event bus has no parent. */ protected EventBus getParentEventBus() { return parentEventBus; } @Override public String toString() { return String.format("%s[id=%x, eventScope=%s, parentEventBus=%s]", getClass().getSimpleName(), System.identityHashCode(this), eventScope, parentEventBus); } /** * Default implementation of {@link org.vaadin.spring.events.EventBus.ApplicationEventBus}. */ public static class DefaultApplicationEventBus extends ScopedEventBus implements ApplicationEventBus { public DefaultApplicationEventBus() { super(EventScope.APPLICATION); } } /** * Default implementation of {@link org.vaadin.spring.events.EventBus.SessionEventBus}. */ public static class DefaultSessionEventBus extends ScopedEventBus implements SessionEventBus { public DefaultSessionEventBus(ApplicationEventBus parentEventBus) { super(EventScope.SESSION, parentEventBus); } } /** * Default implementation of {@link org.vaadin.spring.events.EventBus.UIEventBus}. */ public static class DefaultUIEventBus extends ScopedEventBus implements UIEventBus { public DefaultUIEventBus(SessionEventBus parentEventBus) { super(EventScope.UI, parentEventBus); } } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/support/ApplicationContextEventBroker.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.support; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.context.ApplicationEvent; import org.springframework.context.ApplicationListener; import org.vaadin.spring.events.EventBus; /** * An {@link org.springframework.context.ApplicationListener} that will forward all received events to an {@link org.vaadin.spring.events.EventBus}. * * @author Petter Holmström (petter@vaadin.com) */ public class ApplicationContextEventBroker implements ApplicationListener { private Log logger = LogFactory.getLog(getClass()); private final EventBus eventBus; public ApplicationContextEventBroker(EventBus eventBus) { this.eventBus = eventBus; } @Override public void onApplicationEvent(ApplicationEvent event) { logger.debug(String.format("Propagating application event [%s] to event bus [%s]", event, this)); eventBus.publish(event.getSource(), event); } } ================================================ FILE: eventbus/src/main/java/org/vaadin/spring/events/support/VaadinEventBusAwareProcessor.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.support; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.context.ConfigurableApplicationContext; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.EventBusAware; import java.security.AccessControlContext; import java.security.AccessController; import java.security.PrivilegedAction; /** * {@link org.springframework.beans.factory.config.BeanPostProcessor} * implementation that passes the corresponding EventBus to beans that * implement the one of the {@link org.vaadin.spring.events.EventBusAware} interfaces * * @author Gert-Jan Timmer (gjr.timmer@gmail.com) * @author Petter Holmström (petter@vaadin.com) */ public class VaadinEventBusAwareProcessor implements ApplicationContextAware, BeanPostProcessor { private ConfigurableApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = (ConfigurableApplicationContext) applicationContext; } @Override public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException { AccessControlContext acc = null; if (System.getSecurityManager() != null && (bean instanceof EventBusAware)) { acc = this.applicationContext.getBeanFactory().getAccessControlContext(); } if (acc != null) { AccessController.doPrivileged((PrivilegedAction) () -> { invokeAwareInterfaces(bean); return null; }, acc); } else { invokeAwareInterfaces(bean); } return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { return bean; } private void invokeAwareInterfaces(Object bean) { if (bean instanceof EventBusAware) { if (bean instanceof EventBusAware.ApplicationEventBusAware) { ((EventBusAware.ApplicationEventBusAware) bean).setApplicationEventBus(this.applicationContext.getBean(EventBus.ApplicationEventBus.class)); } if (bean instanceof EventBusAware.SessionEventBusAware) { ((EventBusAware.SessionEventBusAware) bean).setSessionEventBus(this.applicationContext.getBean(EventBus.SessionEventBus.class)); } if (bean instanceof EventBusAware.UIEventBusAware) { ((EventBusAware.UIEventBusAware) bean).setUIEventBus(this.applicationContext.getBean(EventBus.UIEventBus.class)); } } } } ================================================ FILE: eventbus/src/test/java/org/vaadin/spring/events/integration/ScopedEventBusIntegrationTest.java ================================================ package org.vaadin.spring.events.integration; import com.vaadin.flow.component.UI; import com.vaadin.flow.server.VaadinService; import com.vaadin.flow.server.VaadinSession; import com.vaadin.flow.spring.SpringVaadinSession; import com.vaadin.flow.spring.annotation.EnableVaadin; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit.jupiter.SpringExtension; import org.vaadin.spring.events.EventBus; import org.vaadin.spring.events.config.EventBusConfiguration; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.mockito.Mockito.when; /** * Test cases for the different event bus scopes * * @author erik@vaadin.com * @since 14/02/2019 */ @ExtendWith(SpringExtension.class) @ContextConfiguration(classes = {EventBusConfiguration.class, ScopedEventBusIntegrationTest.Config.class}) class ScopedEventBusIntegrationTest { @Configuration @EnableVaadin public static class Config { } public static class TestSession extends SpringVaadinSession { TestSession() { super(Mockito.mock(VaadinService.class)); } @Override public boolean hasLock() { return true; } @Override public void lock() { } @Override public void unlock() { } } private static int uiId = 0; @Autowired ApplicationContext applicationContext; @BeforeEach public void setUp() { MockitoAnnotations.initMocks(this); VaadinSession.setCurrent(new TestSession()); UI.setCurrent(createMockUI()); } @Test void testSameUIReturnsSameUIEventBus() { EventBus.UIEventBus uiBus = applicationContext.getBean(EventBus.UIEventBus.class); EventBus.UIEventBus uiBus2 = applicationContext.getBean(EventBus.UIEventBus.class); assertEquals(uiBus, uiBus2, "Same UI should return same UIEventBus"); } @Test void testDifferentUIReturnsDifferentUIEventBus() { EventBus.UIEventBus uiBus = applicationContext.getBean(EventBus.UIEventBus.class); UI.setCurrent(createMockUI()); EventBus.UIEventBus uiBus2 = applicationContext.getBean(EventBus.UIEventBus.class); assertNotEquals(uiBus, uiBus2, "Different UIs should return different UIEventBuses"); } @Test void testNoUIThrowsBeanCreationException() { UI.setCurrent(null); assertThrows(BeanCreationException.class, () -> applicationContext.getBean(EventBus.UIEventBus.class)); } @Test void testSameSessionReturnsSameSessionEventBus() { EventBus.SessionEventBus sessionBus = applicationContext.getBean(EventBus.SessionEventBus.class); EventBus.SessionEventBus sessionBus2 = applicationContext.getBean(EventBus.SessionEventBus.class); assertEquals(sessionBus, sessionBus2, "Same session should return same SessionEventBus"); } @Test void testSameSessionDifferentUIReturnsSameSessionEventBus() { EventBus.SessionEventBus sessionBus = applicationContext.getBean(EventBus.SessionEventBus.class); UI.setCurrent(createMockUI()); EventBus.SessionEventBus sessionBus2 = applicationContext.getBean(EventBus.SessionEventBus.class); assertEquals(sessionBus, sessionBus2, "Same session different UIs should return same SessionEventBus"); } @Test void testDifferentSessionsReturnDifferentSessionEventBuses() { EventBus.SessionEventBus sessionBus = applicationContext.getBean(EventBus.SessionEventBus.class); VaadinSession.setCurrent(new TestSession()); EventBus.SessionEventBus sessionBus2 = applicationContext.getBean(EventBus.SessionEventBus.class); assertNotEquals(sessionBus, sessionBus2, "Different sessions should return different SessionEventBuses"); } @Test void sameApplicationReturnsSameApplicationEventBus() { EventBus.ApplicationEventBus applicationBus = applicationContext.getBean(EventBus.ApplicationEventBus.class); EventBus.ApplicationEventBus applicationBus2 = applicationContext.getBean(EventBus.ApplicationEventBus.class); assertEquals(applicationBus, applicationBus2, "ApplicationEventBus should always be the same"); } @Test void sameApplicationDifferentUIReturnsSameApplicationEventBus() { EventBus.ApplicationEventBus applicationBus = applicationContext.getBean(EventBus.ApplicationEventBus.class); UI.setCurrent(createMockUI()); EventBus.ApplicationEventBus applicationBus2 = applicationContext.getBean(EventBus.ApplicationEventBus.class); assertEquals(applicationBus, applicationBus2, "ApplicationEventBus should always be the same"); } @Test void sameApplicationDifferentSessionReturnsSameApplicationEventBus() { EventBus.ApplicationEventBus applicationBus = applicationContext.getBean(EventBus.ApplicationEventBus.class); VaadinSession.setCurrent(new TestSession()); EventBus.ApplicationEventBus applicationBus2 = applicationContext.getBean(EventBus.ApplicationEventBus.class); assertEquals(applicationBus, applicationBus2, "ApplicationEventBus should always be the same"); } /** * Creates a new mock UI with a unique ID */ private UI createMockUI() { UI ui = Mockito.mock(UI.class); int id = uiId++; when(ui.getUIId()).thenReturn(id); return ui; } } ================================================ FILE: eventbus/src/test/java/org/vaadin/spring/events/internal/ScopedEventBusTest.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.internal; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import org.vaadin.spring.events.Event; import org.vaadin.spring.events.EventBusListener; import org.vaadin.spring.events.EventScope; import org.vaadin.spring.events.HierachyTopicFilter; import org.vaadin.spring.events.annotation.EventBusListenerMethod; import org.vaadin.spring.events.annotation.EventBusListenerTopic; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; /** * Test case for {@link org.vaadin.spring.events.internal.ScopedEventBus}. * * @author Petter Holmström (petter@vaadin.com) */ public class ScopedEventBusTest { ScopedEventBus.DefaultApplicationEventBus applicationEventBus; ScopedEventBus.DefaultSessionEventBus sessionEventBus; interface StringListener extends EventBusListener { } static class MultipleListeners { Event theStringEvent; Event theIntegerEvent; String theStringPayload; Integer theIntegerPayload; Event theStringEventWithTopic; Event theIntegerEventWithTopic; String theStringPayloadWithTopic; Integer theIntegerPayloadWithTopic; String theStringPayloadWithTopicFail; Integer theIntegerPayloadWithTopicFail; @EventBusListenerMethod void onStringEvent(Event stringEvent) { theStringEvent = stringEvent; } @EventBusListenerMethod void onStringPayloadEvent(String stringPayload) { theStringPayload = stringPayload; } @EventBusListenerMethod void onIntegerEvent(Event integerEvent) { theIntegerEvent = integerEvent; } @EventBusListenerMethod void onIntegerPayloadEvent(Integer integerPayload) { theIntegerPayload = integerPayload; } @EventBusListenerTopic(topic = "shouldSucceed") @EventBusListenerMethod void onStringEventWithTopic(Event stringEvent) { theStringEventWithTopic = stringEvent; } @EventBusListenerTopic(topic = "shouldSucceed") @EventBusListenerMethod void onStringPayloadEventWithTopic(String stringPayload) { theStringPayloadWithTopic = stringPayload; } @EventBusListenerTopic(topic = "shouldSucceed") @EventBusListenerMethod void onIntegerEventWithTopic(Event integerEvent) { theIntegerEventWithTopic = integerEvent; } @EventBusListenerTopic(topic = "shouldSucceed", filter = HierachyTopicFilter.class) @EventBusListenerMethod void onIntegerPayloadEventWithTopic(Integer integerPayload) { theIntegerPayloadWithTopic = integerPayload; } @EventBusListenerTopic(topic = "shouldFail") @EventBusListenerMethod void onStringPayloadEventWithTopicFail(String stringPayload) { theStringPayloadWithTopicFail = stringPayload; } @EventBusListenerTopic(topic = "shouldSucceed.butFail") @EventBusListenerMethod void onIntegerPayloadEventWithTopicFail(Integer integerPayload) { theIntegerPayloadWithTopicFail = integerPayload; } } class TopicStringListener implements EventBusListener { Event theStringEvent; @Override public void onEvent(Event event) { this.theStringEvent = event; } } class TopicIntegerListener implements EventBusListener { Event theIntegerEvent; @Override public void onEvent(Event event) { this.theIntegerEvent = event; } } class TopicListeners { String theStringPayload; Integer theIntegerPayload; Event theStringEvent; Event theIntegerEvent; @EventBusListenerMethod void onStringEvent(String theStringPayload) { this.theStringPayload = theStringPayload; } @EventBusListenerMethod void onStringEvent(Event theStringEvent) { this.theStringEvent = theStringEvent; } @EventBusListenerMethod void onIntegerEvent(Integer theIntegerPayload) { this.theIntegerPayload = theIntegerPayload; } @EventBusListenerMethod void onIntegerEvent(Event theIntegerEvent) { this.theIntegerEvent = theIntegerEvent; } } static class InvalidListener1 { @EventBusListenerMethod void tooFewParameters() { } } static class InvalidListener2 { @EventBusListenerMethod void tooManyParameters(String parameter1, Integer parameter2) { } } @BeforeEach public void setUp() { applicationEventBus = new ScopedEventBus.DefaultApplicationEventBus(); sessionEventBus = new ScopedEventBus.DefaultSessionEventBus(applicationEventBus); } @AfterEach public void tearDown() { sessionEventBus.destroy(); applicationEventBus.destroy(); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) public void testSubscribeAndPublish() { StringListener stringListener = mock(StringListener.class); sessionEventBus.subscribe(stringListener); sessionEventBus.publish(this, "Hello World"); ArgumentCaptor event = ArgumentCaptor.forClass(Event.class); verify(stringListener).onEvent(event.capture()); assertEquals("Hello World", event.getValue().getPayload()); } @Test public void testSubscribeAndPublishWithListenerMethods() { MultipleListeners listener = new MultipleListeners(); sessionEventBus.subscribe(listener); sessionEventBus.publish(this, "Hello World"); assertNull(listener.theIntegerEvent); assertNull(listener.theIntegerPayload); assertNotNull(listener.theStringEvent); assertEquals("Hello World", listener.theStringEvent.getPayload()); assertEquals("Hello World", listener.theStringPayload); } @Test public void testSubscribeAndPublishWithListenerMethodsWithTopic() { MultipleListeners listener = new MultipleListeners(); sessionEventBus.subscribe(listener); sessionEventBus.publish("shouldSucceed", this, "Hello World"); sessionEventBus.publish("shouldSucceed.int", this, 10); // null because not called with topic assertNull(listener.theStringPayload); assertNull(listener.theStringEvent); assertNull(listener.theIntegerPayload); assertNull(listener.theIntegerEvent); // null because topic must fail assertNull(listener.theStringPayloadWithTopicFail); assertNull(listener.theIntegerPayloadWithTopicFail); assertNull(listener.theIntegerEventWithTopic); assertNotNull(listener.theStringPayloadWithTopic); assertNotNull(listener.theStringEventWithTopic); assertEquals("Hello World", listener.theStringPayloadWithTopic); assertEquals("Hello World", listener.theStringEventWithTopic.getPayload()); assertEquals(10, listener.theIntegerPayloadWithTopic.intValue()); } @Test public void testSubscribeToTopicAndPublishWithTopic() { String topic = "/news"; String payload = "Hello World"; TopicListeners topicListener = new TopicListeners(); applicationEventBus.subscribe(topicListener, topic); applicationEventBus.publish(topic, this, payload); assertEquals(payload, topicListener.theStringPayload); assertEquals(payload, topicListener.theStringEvent.getPayload()); assertNull(topicListener.theIntegerPayload); assertNull(topicListener.theIntegerEvent); } @Test public void testUnsubscribeFromTopicAndPublishWithTopic() { String topic = "/news"; String payload = "Hello World"; TopicListeners topicListener = new TopicListeners(); applicationEventBus.subscribe(topicListener, topic); applicationEventBus.publish(topic, this, payload); assertEquals(payload, topicListener.theStringPayload); topicListener.theStringPayload = null; applicationEventBus.unsubscribe(topicListener); applicationEventBus.publish(topic, this, payload); assertNotEquals(payload, topicListener.theStringPayload); } @Test public void testSubscribeToTopicAndPublishWithDifferentTopic() { String topic = "/news"; String differentTopic = "/different"; String payload = "Hello World"; TopicListeners topicListener = new TopicListeners(); applicationEventBus.subscribe(topicListener, topic); applicationEventBus.publish(differentTopic, this, payload); assertNotEquals(payload, topicListener.theStringPayload); } @Test public void testSubscribeTopicListenerForTwoTopicsAndPublishWithThoseTopics() { String newsTopic = "/news"; String counterTopic = "/counter"; String newsPayload = "Hello World"; Integer counterPayload = 0; TopicListeners topicListener = new TopicListeners(); applicationEventBus.subscribe(topicListener, newsTopic); applicationEventBus.subscribe(topicListener, counterTopic); applicationEventBus.publish(newsTopic, this, newsPayload); assertEquals(newsPayload, topicListener.theStringPayload); assertNotEquals(counterPayload, topicListener.theIntegerPayload); applicationEventBus.publish(counterTopic, this, counterPayload); assertEquals(counterPayload, topicListener.theIntegerPayload); } @Test public void testTwoTopicListenersSubscribedForSameTopic() { String topic = "/news"; String payload = "Hello World"; TopicListeners firstTopicListener = new TopicListeners(); TopicListeners secondTopicListener = new TopicListeners(); applicationEventBus.subscribe(firstTopicListener, topic); applicationEventBus.subscribe(secondTopicListener, topic); applicationEventBus.publish(topic, this, payload); assertEquals(payload, firstTopicListener.theStringPayload); assertEquals(payload, firstTopicListener.theStringEvent.getPayload()); assertEquals(payload, secondTopicListener.theStringPayload); assertEquals(payload, secondTopicListener.theStringEvent.getPayload()); } @Test public void testTwoTopicListenersSubscribedForDifferentTopics() { String firstTopic = "/first"; String secondTopic = "/second"; String firstPayload = "first"; String secondPayload = "second"; TopicListeners firstTopicListener = new TopicListeners(); TopicListeners secondTopicListener = new TopicListeners(); applicationEventBus.subscribe(firstTopicListener, firstTopic); applicationEventBus.subscribe(secondTopicListener, secondTopic); applicationEventBus.publish(firstTopic, this, firstPayload); assertEquals(firstPayload, firstTopicListener.theStringPayload); assertEquals(firstPayload, firstTopicListener.theStringEvent.getPayload()); assertNull(secondTopicListener.theStringPayload); assertNull(secondTopicListener.theStringEvent); applicationEventBus.publish(secondTopic, this, secondPayload); assertEquals(secondPayload, secondTopicListener.theStringPayload); assertEquals(secondPayload, secondTopicListener.theStringEvent.getPayload()); assertNotEquals(secondPayload, firstTopicListener.theStringPayload); assertNotEquals(secondPayload, firstTopicListener.theStringEvent.getPayload()); } @Test public void testSubscribeAndPublishWithTopic() { String topic = "/news"; String payload = "Hello World"; TopicStringListener stringListener = new TopicStringListener(); sessionEventBus.subscribe(stringListener, topic); sessionEventBus.publish(topic, this, payload); assertEquals(payload, stringListener.theStringEvent.getPayload()); } @Test public void testSubscribeAndPublishWithDifferentTopic() { String topic = "/news"; String payload = "Hello World"; String differentTopic = payload + ".extension"; TopicStringListener stringListener = new TopicStringListener(); sessionEventBus.subscribe(stringListener, topic); sessionEventBus.publish(differentTopic, this, payload); assertNull(stringListener.theStringEvent); } @Test public void testSubscribeAndPublishWithSameTopicButDifferentPayloadType() { String topic = "/news"; Integer payload = 0; TopicStringListener stringListener = new TopicStringListener(); sessionEventBus.subscribe(stringListener, topic); sessionEventBus.publish(topic, this, payload); assertNull(stringListener.theStringEvent); } @Test public void testSubscribeTwoTopicListenersForSameTopicWithDifferentPayloadType() { String topic = "/news"; String stringPayload = "Hello World"; Integer integerPayload = 0; TopicStringListener stringListener = new TopicStringListener(); TopicIntegerListener integerListener = new TopicIntegerListener(); sessionEventBus.subscribe(stringListener, topic); sessionEventBus.subscribe(integerListener, topic); sessionEventBus.publish(topic, this, integerPayload); assertNotNull(integerListener.theIntegerEvent); assertNull(stringListener.theStringEvent); sessionEventBus.publish(topic, this, stringPayload); assertNotNull(integerListener.theIntegerEvent); assertNotNull(stringListener.theStringEvent); } @Test public void testSubscribeAndPublishWithListenerMethodsAndTooFewParameters() { assertThrows(IllegalArgumentException.class, () -> sessionEventBus.subscribe(new InvalidListener1())); } @Test public void testSubscribeAndPublishWithListenerMethodsAndTooManyParameters() { assertThrows(IllegalArgumentException.class, () -> sessionEventBus.subscribe(new InvalidListener2())); } @Test public void testPublishToInvalidScope() { assertThrows(UnsupportedOperationException.class, () -> applicationEventBus.publish(EventScope.SESSION, this, "fail")); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) public void testPublishToParentScope() { StringListener stringListener = mock(StringListener.class); applicationEventBus.subscribe(stringListener); sessionEventBus.publish(EventScope.APPLICATION, this, "Hello World"); ArgumentCaptor event = ArgumentCaptor.forClass(Event.class); verify(stringListener).onEvent(event.capture()); assertEquals("Hello World", event.getValue().getPayload()); } @Test public void testPublishToParentScopeWithListenerMethods() { MultipleListeners listener = new MultipleListeners(); applicationEventBus.subscribe(listener); sessionEventBus.publish(EventScope.APPLICATION, this, "Hello World"); assertNull(listener.theIntegerEvent); assertNull(listener.theIntegerPayload); assertNotNull(listener.theStringEvent); assertEquals("Hello World", listener.theStringEvent.getPayload()); assertEquals("Hello World", listener.theStringPayload); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) public void testPropagateToChild() { StringListener stringListener = mock(StringListener.class); sessionEventBus.subscribe(stringListener); applicationEventBus.publish(this, "Hello World"); ArgumentCaptor event = ArgumentCaptor.forClass(Event.class); verify(stringListener).onEvent(event.capture()); assertEquals("Hello World", event.getValue().getPayload()); } @Test public void testPropagateToChildWithListenerMethods() { MultipleListeners listener = new MultipleListeners(); applicationEventBus.subscribe(listener); applicationEventBus.publish(this, "Hello World"); assertNull(listener.theIntegerEvent); assertNull(listener.theIntegerPayload); assertNotNull(listener.theStringEvent); assertEquals("Hello World", listener.theStringEvent.getPayload()); assertEquals("Hello World", listener.theStringPayload); } @Test @SuppressWarnings({"unchecked", "rawtypes"}) public void testNoPropagationToChild() { StringListener stringListener = mock(StringListener.class); sessionEventBus.subscribe(stringListener, false); applicationEventBus.publish(this, "Hello World"); sessionEventBus.publish(this, "Hello World"); ArgumentCaptor event = ArgumentCaptor.forClass(Event.class); verify(stringListener, only()).onEvent(event.capture()); verifyNoMoreInteractions(stringListener); assertEquals("Hello World", event.getValue().getPayload()); } @Test public void testNoPropagationToChildWithListenerMethods() { MultipleListeners listener = new MultipleListeners(); sessionEventBus.subscribe(listener, false); sessionEventBus.publish(this, "Hello World Session"); applicationEventBus.publish(this, "Hello World Application"); assertNull(listener.theIntegerEvent); assertNull(listener.theIntegerPayload); assertNotNull(listener.theStringEvent); assertEquals("Hello World Session", listener.theStringEvent.getPayload()); assertEquals("Hello World Session", listener.theStringPayload); } @Test public void testSubscribeWithWeakReference() { StringListener listener = new StringListener() { int cnt = 0; @Override public void onEvent(Event event) { cnt++; if (cnt > 1) { fail("I should have been garbage collected by now"); } } }; applicationEventBus.subscribeWithWeakReference(listener); applicationEventBus.publish(this, "Hello World Application"); listener = null; System.gc(); applicationEventBus.publish(this, "Hello World Application"); } } ================================================ FILE: eventbus/src/test/java/org/vaadin/spring/events/support/ApplicationContextEventBrokerTest.java ================================================ /* * Copyright 2015 The original authors * * 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 org.vaadin.spring.events.support; import org.junit.jupiter.api.Test; import org.springframework.context.ApplicationEvent; import org.vaadin.spring.events.EventBus; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; /** * Test case for {@link org.vaadin.spring.events.support.ApplicationContextEventBroker}. * * @author Petter Holmström (petter@vaadin.com) */ public class ApplicationContextEventBrokerTest { @Test public void testOnApplicationEvent() { ApplicationEvent event = new ApplicationEvent(this) { private static final long serialVersionUID = 7475015652750718692L; @Override public Object getSource() { return "mySource"; } }; EventBus eventBus = mock(EventBus.class); new ApplicationContextEventBroker(eventBus).onApplicationEvent(event); verify(eventBus).publish("mySource", event); } } ================================================ FILE: pom.xml ================================================ 4.0.0 org.vaadin.spring parent-pom 14.0.1-SNAPSHOT pom Vaadin4Spring Additions to the official Vaadin Spring add-on. https://github.com/peholmst/vaadin4spring Apache License 2.0 http://www.apache.org/licenses/LICENSE-2.0.html https://github.com/peholmst/vaadin4spring scm:git:git@github.com:peholmst/vaadin4spring.git scm:git:git@github.com:peholmst/vaadin4spring.git HEAD sonatype-nexus-snapshots https://oss.sonatype.org/content/repositories/snapshots sonatype-nexus-staging https://oss.sonatype.org/service/local/staging/deploy/maven2/ Chris Phillipson fastnsilver@gmail.com Erik Lumme Vaadin erik@vaadin.com Gert-Jan Timmer gjr.timmer@gmail.com Idel Pivnitskiy idel.pivnitskiy@gmail.com Josh Long josh@joshlong.com Marco Luthardt marco.luthardt@iandme.net Matti Tahvonen Vaadin matti@vaadin.com Nicolas Frankel nicolas@frankel.ch Peter Lehto Vaadin peter@vaadin.com Petter Holmström Vaadin petter@vaadin.com Tobias Placht dev@knacht.net UTF-8 1.8 1.8 14.0.1 5.1.2.RELEASE 5.5.1 3.0.0 com.vaadin vaadin-bom ${vaadin.version} pom import org.mockito mockito-core ${mockito.version} org.junit.jupiter junit-jupiter-engine ${junit-jupiter.version} org.springframework spring-test ${spring.version} org.apache.maven.plugins maven-compiler-plugin 3.8.1 ${project.build.encoding} ${project.build.target} ${project.build.source} org.apache.maven.plugins maven-resources-plugin 3.1.0 ${project.build.encoding} org.apache.maven.plugins maven-deploy-plugin 2.8.2 org.apache.maven.plugins maven-surefire-plugin 3.0.0-M3 org.apache.maven.plugins maven-release-plugin 2.5.3 v@{project.version} true release release org.apache.maven.plugins maven-source-plugin 3.1.0 attach-sources verify jar-no-fork org.apache.maven.plugins maven-javadoc-plugin 3.1.1 attach-javadocs jar http://docs.spring.io/spring/docs/${spring.version}/javadoc-api/ https://vaadin.com/api/platform/${vaadin.version}/ -Xdoclint:none UTF-8 org.apache.maven.plugins maven-gpg-plugin 1.5 sign-artifacts verify sign eventbus