I use vaadin + spring IOC + google guava eventbus. Resources recommends to use guava eventbus as singleton. But when I do that I have the following problem;
Let's say I run the application on 3 different browsers at the same time, so I have 3 different instances of my application.
then for example when an I press a button on one browser and fire an event, I notice that my related listener method with the #subscribe annotation gets called 3 times!
Is this is a normal behaviour I would expect because I use eventbus as singleton? If not what is going on here? MainController is a spring managed bean with a a custom Vaadin Application Scope
class MainController{
public MainController() throws DAOException, Exception {
EventBusFactory.getEventBusInstance().register(this);
}
#Subscribe
public void addFacetEvent(FacetAddedEvent event) throws DAOException {
getTreeTableView().addToList(event.getData());
}
}
class EventBusFactory{
public static EventBus getEventBusInstance() {
if(eventBus==null){
eventBus=new EventBus();
}
return eventBus;
}
}
P.s I also hesitate in Vaadin should I to use guava eventbus or guava gwt event bus?
Thanks
Short answer: It's normal and expected behaviour in this configuration (you have three Vaadin Applications and hence three MainController instances managed with single EventBus).
By custom Vaadin Application Scope did you mean scope from this Vaadin addon?
Anyway, it's simple to reproduce your situation having prototype-scoped MainController bean and Vaadin App like this:
public class SandpitApplication extends Application {
private static final long serialVersionUID = 1L;
private static final Logger log = Logger.getLogger(SandpitApplication.class);
// https://vaadin.com/wiki/-/wiki/Main/Spring%20Integration#section-Spring+Integration-SpringContextHelperClass
private SpringContextHelper ctx;
#Override
public void init() {
// vaadin stuff
setTheme("common");
final Window mainWindow = new Window("Vaadin Sample Application");
setMainWindow(mainWindow);
// get your bean from spring
log.info("start SandpitApplication#" + Integer.toHexString(hashCode()));
ctx = new SpringContextHelper(this);
// create application-wide bean
final MainController mainController = ctx.getBean("mainController");
mainWindow.addComponent(new Button("click to post", new Button.ClickListener() {
#Override public void buttonClick(final ClickEvent event) {
log.info("click on button");
EventBusFactory.getEventBusInstance().post(
new FacetAddedEventImpl("click-"
+ new SimpleDateFormat("HH:mm:ss").format(new Date())));
log.info(mainController);
}
}));
}
}
and MainController class:
class MainController {
private static final Logger log = Logger.getLogger(MainController.class);
public MainController() {
log.info("creating MainController#" + Integer.toHexString(hashCode()));
EventBusFactory.getEventBusInstance().register(this);
}
#Subscribe
public void addFacetEvent(final FacetAddedEvent event) {
final String signature = "MC#" + Integer.toHexString(hashCode()) + ": ";
log.info("addFacetEvent in " + signature + event);
// getTreeTableViewBuilder returns extended ArrayList with fancy add
getTreeTableViewBuilder().addFacetToList(signature + event.getData());
}
// plus other stuff like toString etc.
}
When you do the following:
Start the vaadin app in your browser (App#1).
Click the App1#button.
Start another app (App#2).
Click the App2#button
Go back to tab with App#1.
Click the App1#button.
you'll get the following output:
start SandpitApplication#75a5555a
creating MainController#2e98f864
click on button // #1
addFacetEvent in MC#2e98f864: FacetAddedEventImpl#6b527dc6{data: click-13:42:45}
MainController#2e98f864{treeTableViewBuilder: [MC#2e98f864: click-13:42:45]}
start SandpitApplication#3f9e529
creating MainController#2f8d604f
click on button // #2
addFacetEvent in MC#2e98f864: FacetAddedEventImpl#36c1fc67{data: click-13:42:47}
addFacetEvent in MC#2f8d604f: FacetAddedEventImpl#36c1fc67{data: click-13:42:47}
MainController#2f8d604f{treeTableViewBuilder: [MC#2f8d604f: click-13:42:47]}
click on button // #1
addFacetEvent in MC#2e98f864: FacetAddedEventImpl#42d32028{data: click-13:42:49}
addFacetEvent in MC#2f8d604f: FacetAddedEventImpl#42d32028{data: click-13:42:49}
MainController#2e98f864{treeTableViewBuilder: [MC#2e98f864: click-13:42:45, MC#2e98f864: click-13:42:47, MC#2e98f864: click-13:42:49]}
You should now see that singleton EventBus is managing two application-wide MainController beans and each is recieving event (because it's resolved by global EventBus).
Trying to guess what do you want to achieve, I'd say that you need create application-wide event bus bean:
<bean id="eventBus" class="com.google.common.eventbus.EventBus"
scope="vaadinApplication" />
About P.S.: We use standard Guava in our Vaadin project extensively, no need for GWT version.
Related
Scenario
I have a pure E4 application in which I want to select the initial perspective for the user depending on the user's roles. I therefore have a perspective to start with which contains one part only. In that part, I use the #PostConstruct-Method to check the user's roles and afterwards trigger the command for switching perspective:
Initial View
#Inject
private IEclipseContext eclipseContext;
#PostConstruct
public void initialize() {
// checking credentials and retrieving roles come here which is pretty long
// that's why switching perspective is a seperate method
// and EclipseContext is injected to instance instead of method
this.switchPerspective(_usersInitialPerspectiveId)
}
private void switchPerspective(String pTargetPerspectiveId) {
final ECommandService _commandService = this.eclipseContext.get(ECommandService.class);
final EHandlerService _handlerService = this.eclipseContext.get(EHandlerService.class);
final Map<String, Object> _commandParameter = new HashMap<>();
_commandParameter.put(PluginIdConstants.ID_OF_PARAMETER_FOR_SWITCH_PERSPEKTIVE,
pZielPerspektiveId);
final ParameterizedCommand _switchPerspectiveCommand =
_commandService.createCommand(COMMAND_ID_FOR_SWITCH_PERSPECTIVE,
_commandParameter);
_handlerService.executeHandler(_switchPerspectiveCommand);
}
For switching perspective from here, I use the exact same handler as from menu items configured in Application.e4xmi, which looks like this:
Perspective Switch Handler
#Execute
public void execute(final MWindow pWindow,
final EPartService pPartService,
final EModelService pModelService,
#Named(PluginIdConstants.ID_OF_PARAMETER_FOR_SWITCH_PERSPEKTIVE)
final String pPerspectiveId) {
final List<MPerspective> _perspectives =
pModelService.findElements(pWindow, pPerspectiveId, MPerspective.class, null);
if (!(_perspectives.isEmpty())) {
// Show perspective for looked up id
pPartService.switchPerspective(_perspectives.get(0));
}
}
The Problem
The problem is pretty simple: When using the above handler triggered by a menu item, it works as intended and switches perspective. But using the same handler by my initial view (triggering it programmatically) does not switch perspective. I debugged the code to check if the handler gets identical information in both cases and it does.
Maybe my application has not finished starting and that's why the handler has no effect, but if this is the problem, how can I check this?
Any ideas on what I maybe missed are welcome!
Based on Christoph Keimel's hint I could create a working solution (thank you very much!). Here's the code that solves the problem:
#ProcessAdditions
private void switchPerspective(final MApplication pApplication,
final IApplicationContext pApplicationContext,
final EModelService pModelService) {
final MWindow _window =
(MWindow) pModelService.find(PluginIdConstants.WINDOW_ID_FOR_MAIN, pApplication);
final String _appName = pApplicationContext.getBrandingName();
initializeWindowTitle(_window, _appName);
final MPerspectiveStack pPerspectiveStack =
(MPerspectiveStack) pModelService.find(PluginIdConstants.PERSPECTIVE_STACK_ID_FOR_MAIN,
pAnwendung);
for (final MPerspective _perspective : pPerspectiveStack.getChildren()) {
if (_perspektive.getElementId().equalsIgnoreCase(this.startingPerspectiveId)) {
pPerspectiveStack.setSelectedElement(_perspective);
break;
}
}
}
On how to register a LifeCycleHandler you can take a look at Lars Vogel's Tutorial.
My main problem finding this solution was how to access the perspective stack. As the UI is not up while the method annotated with ProcessAdditions is running, I have to access the application model via the MApplication type - which is the root element of my application model. Combining the EModelService I can access all UI elements I want and manipulate them accordingly.
Injecting any UI element like the MPerspectiveStack or the MWindow leads to a skipped method as these result in null values due to not being initalized yet.
I am using Vaadin 7 with CDI. Everything worked perfectly until I wanted to use other UI class in my project for dynamic document generation.
#CDIUI(value = "PrintUI")
public static class PrintUI extends UI
{
#EJB
MyBean myBean;
#Override
protected void init(VaadinRequest request)
{
setContent(new Label(myBean.getHTMLContent(), ContentMode.HTML));
}
}
//method somewhere in the main UI class custom component
void printOpenedPage ()
{
// Create an opener extension
BrowserWindowOpener opener = new BrowserWindowOpener(PrintUI.class);
opener.setFeatures("height=200,width=400,resizable");
// A button to open the printer-friendly page.
Button print = new Button("Click to Print");
opener.extend(print);
}
My problem is that when I open the window I always get NPE because MyBean object in't injected. I tried to use #Inject and #EJB. Also tried to inject a local interface of the class, but no luck. Is it possible to access Stateless ejb objects in UI class that is opened trough BrowserWindowOpener class?
Thanks in advance
I am creating eclipse RCP 4.x application. Application consist of multiple perspectives. I want to open default perspective programmatically depending on some condition. Below code is capable of loading perspective.
#Execute
public void execute(MApplication app, EPartService partService,
EModelService modelService) {
MPerspective element =
(MPerspective) modelService.find("com.sanyotechnologyindia.desktop.app.perspective.enduser", app);
// now switch perspective
partService.switchPerspective(element);
}
But I can not put this code in method which is annotated with #PostContextCreate.
Can you suggest any solution for this?
================
As per solution suggested by Greg, I tried following code in Application Lifecycle class.
#ProcessAdditions
void processAdditions(MApplication app, EPartService partService,
EModelService modelService){
MPerspective element =
(MPerspective) modelService.find("com.sanyotechnologyindia.desktop.app.perspective.usermanagement", app);
// now switch perspective
partService.switchPerspective(element);
}
Now I am getting following error at line partService.switchPerspective(element);
java.lang.IllegalStateException: Application does not have an active window
================Update:==================
Added org.eclipse.osgi.services plugin to dependencies.
#PostContextCreate
public void postContextContext(IEventBroker eventBroker)
{
eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE,
new AppStartupCompleteEventHandler());
}
private class AppStartupCompleteEventHandler implements EventHandler
{
#Inject private MApplication app;
#Inject private EPartService partService;
#Inject private EModelService modelService;
#Override
public void handleEvent(Event arg0) {
MPerspective element =
(MPerspective) modelService.find("com.sanyotechnologyindia.desktop.app.perspective.usermanagement", app);
partService.switchPerspective(element);
}
}
However now framework not able to inject MApplication,EPartService and EModelService in AppStartupCompleteEventHandler instance.
If you only want to do this in your life cycle class try putting it in a #ProcessAdditions method rather than #PostContextCreate. #ProcessAdditions runs later in the life cycle just before the model is rendered.
Update:
Even #PostAdditions is too early to do some UI operations. You need to wait for the application start complete event. You can subscribe to this event using the event broker in the #PostContextCreate method:
#PostContextCreate
public void postContextContext(IEventBroker eventBroker)
{
eventBroker.subscribe(UIEvents.UILifeCycle.APP_STARTUP_COMPLETE,
new AppStartupCompleteEventHandler());
}
private class AppStartupCompleteEventHandler implements EventHandler
{
#Override
public void handleEvent(final Event event)
{
// TODO do UI operations here
}
}
EventHandler is org.osgi.service.event.EventHandler.
Update:
If you want to use injection in the event handler you must create the handler using `ContextInjectionFactory':
EventHandler handler = ContextInjectionFactory.make(AppStartupCompleteEventHandler.class, context);
where context is the IEclipseContext.
Note: You can't use this for a non-static inner class, instead use:
EventHandler handler = new AppStartupCompleteEventHandler();
ContextInjectionFactory.inject(handler, context);
This method does not support injection on the constructor.
I have a simple application and want to make it testable. I m new in this area.
Here is a simple Presenter, taking in mind this code ,could you advice or give me some example how to test it.
public class SomePresenter extends Presenter<MainPanelPresenter.Display>
{
public interface Display extends WidgetDisplay
{
HasClickHandlers getAddButton();
HasClickHandlers getDeleteButton();
void setData(ArrayList<Person> data);
ArrayList<String> getSelectedRows();
Widget asWidget();
}
private final DispatchAsync dispatcher;
public static final Place PLACE = new Place("main");
#Inject
public SomePresenter(DispatchAsync dispatcher, EventBus eventBus, Display display)
{
super(display, eventBus);
this.dispatcher = dispatcher;
bind();
}
protected void onBind()
{
display.getAddButton().addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
eventBus.fireEvent(new AddButtonEvent());
}
});
display.getDeleteButton().addClickHandler(new ClickHandler()
{
public void onClick(ClickEvent event)
{
ArrayList<String> list = display.getSelectedRows();
deletePerson(list);
}
});
}
....
private void loadDbData()
{
..........
}
private void deletePerson(ArrayList<String> ids)
{
..........
}
}
Edit:
What does the Presenter is, load initial data from db, have 2 buttons add and delete.
When add is press then a new form is load and user is able to input data and save to the db,
delete button just delete person from db.
Thanks
The general idea of unit testing such a class would be, like for any other class :
create Mock version of the dependencies (Display, EventBus, etc...)
set expectations on what the depdencies should do when the Presenter works
exercice the Presenter and check the expectations
However there are a couple of issues with your version of the Presenter :
The loadDbData() method is not showed, but I assumed it means the Presenter also has access to some other component that does the fetching. Can this component be abtracted in a dependency, and mocked liked the rest ?
Then there is the testing of bind(). The only responsibility of your Presenter in this method is to set up callbacks on some buttons provided by the Display. What you want to test is both :
That the callbacks are set
That the set callbacks do the expected things
A few ideas to help with the later :
You can reduce the coupling between Presenter and Button. If possible, change the Display interface from :
Button getAddButton();
to
addAddButtonClickedHandler(ClickHandler);
This means your Presenter does not have to use a Display object that returns actual BUtton
You can reduce the callbacks content to calling a single method, that you can then test in isolation
protected void bind() {
display.addAddButtonClickHandler(new ClickHandler() {
public void onClick(ClickEvent) {
fireAdded();
}
});
}
// The fireAdded function can be tested independenty of the Display, potentially with
// a mock EventBus
protected void fireAdded() {
event.fireEvent(....)
}
If you really want to check that the callbacks are properly set, than you can use a 'Dummy' implementation of the Display class, that provides you a list of all the callbacks, and let you call them
private class DummyDisplay implements Display {
private List<ClickHandler> addButtonClickHandlers;
public void addAddButtonClickHandler(ClickHandler handler) {
addButtonClickHandlers.add(handler);
}
public void fireAddButtonClick() {
for (ClickHandler h in addButtonClickHandlers) {
h.onClick(new ClickEvent());
}
}
// ....
}
Then your test would :
create a presenter with such a dummy display
use bind to set the callbacks
use display.fireAddButtonClick() to simulate a user clicking
check that has the result of the click, the effects of fireAdded are seen
This type of class (that mostly glue other classes together) can tend to be hard to test ; at some point, it the other classes are thoroughly tested it can become slightly counter productive to concentrate on the gluers, rather than the glued.
Hoping this helps.
I wonder how to use the EventBus or whether there are some better solutions to send an Event through the project.
Widget1 has a Button. Widget2 has a Label, that should change when I press the button. These widgets are in a DockLayout:
RootLayoutPanel rootLayoutPanel = RootLayoutPanel.get();
DockLayoutPanel dock = new DockLayoutPanel(Unit.EM);
dock.addWest(new Widget1(), 10);
dock.add(new Widget2());
rootLayoutPanel.add(dock);
I have declared an handleClickAlert in Widget1:
#UiHandler("button")
void handleClickAlert(ClickEvent e) {
//fireEvent(e);
}
When you divide the project into logical parts (for example with MVP), then the different parts sometimes need to communicate. Typical this communication is done by sending status changes, e.g.:
user logged-in / logged-out.
user navigated directly via URL to a page, so the menu needs to be updated.
Using the event bus is quite logical in those cases.
To use it you instantiate one EventBus per app which is then used by all other classes. To achieve this use a static field, factory or dependency injection (GIN in case of GWT).
Example with your own event types:
public class AppUtils{
public static EventBus EVENT_BUS = GWT.create(SimpleEventBus.class);
}
Normally you'd also create your own event types and handlers:
public class AuthenticationEvent extends GwtEvent<AuthenticationEventHandler> {
public static Type<AuthenticationEventHandler> TYPE = new Type<AuthenticationEventHandler>();
#Override
public Type<AuthenticationEventHandler> getAssociatedType() {
return TYPE;
}
#Override
protected void dispatch(AuthenticationEventHandler handler) {
handler.onAuthenticationChanged(this);
}
}
and the handler:
public interface AuthenticationEventHandler extends EventHandler {
void onAuthenticationChanged(AuthenticationEvent authenticationEvent);
}
Then you use it like this:
AppUtils.EVENT_BUS.addHandler(AuthenticationEvent.TYPE, new AuthenticationEventHandler() {
#Override
public void onAuthenticationChanged(AuthenticationEvent authenticationEvent) {
// authentication changed - do something
}
});
and fire the event:
AppUtils.EVENT_BUS.fireEvent(new AuthenticationEvent());