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.
Related
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
The problem
A presenter that "manages" a passive view subscribes to events that occur in that view (e.g. button click), and does not directly expose the methods that handle those events as public interface. I don't like the idea to make those methods public just for unit-testing since it smells like exposing the internal implementation details. Therefore, calling that event handling code becomes quite non-trivial.
My solution
The view mock has to "intercept" the event subscription and then the corresponding intercepted listener is used to call the event handling code. My implementation includes a utility class that implements the Answer interface from the Mockito API
private class ArgumentRetrievingAnswer<TArg> implements Answer {
private TArg _arg;
#Override
public Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
_arg = (TArg)args[0];
return null;
}
public TArg getArg() {
return _arg;
}
}
The event subscription is intercepted in the following way
XyzView xyzView = mock(XyzView.class);
ArgumentRetrievingAnswer<OnEventListener> xyzViewTouchedListenerInterceptor =
new ArgumentRetrievingAnswer<OnEventListener>();
doAnswer(xyzViewTouchedListenerInterceptor)
.when(xyzView).addViewTouchedListener(any(OnEventListener.class));
After creating the SUT instance...
XyzPresenter sut = new XyzPresenter(xyzView);
...I obtain the listener
OnEventListener xyzViewTouchListener = xyzViewTouchedListenerInterceptor.getArg();
In the "Act" part I call the event handling method of the listener
xyzViewTouchListener.onEvent();
The question
I'm quite new to unit testing in Java, so I'd like to know if there's any more elegant way of testing the presenter code. The current "Arrange" part is quite bloated an does not seem to excel in readability.
Edit:
Adding the simplified SUT code on Jonathan's request. It illustrates that the presenter does not have any public methods (except constructor), and subscribes to the view events.
public interface XyzView {
void setInfoPanelCaptionText(String text);
void addViewInitializedListener(OnEventListener listener);
void addViewTouchedListener(OnEventListener listener);
}
public class XyzPresenter {
private XyzView _xyzView;
private OnEventListener _xyzViewTouchedListener = new OnEventListener() {
#Override
public void onEvent() {
handleXyzViewTouch();
}
};
public XyzPresenter(XyzView xyzView) {
_xyzView = xyzView;
_xyzView.addViewTouchedListener(_xyzViewTouchedListener);
}
private void handleXyzViewTouch() {
// event handling code
}
}
Basically I also use ArgumentCaptor in this setup.
The basic layout of my presenter tests is like this :
#RunWith(MockitoJUnitRunner.class)
public class PresenterTest {
private Presenter sut;
#Mock
private View view;
#Captor
private ArgumentCaptor<ViewTouchedListener> listenerCaptor;
private ViewTouchedListener listener;
#Before
public void setUp() {
sut = new Presenter(view);
verify(view).addViewTouchedListener(listenerCaptor.capture());
listener = listenerCaptor.getValue();
}
// test methods have access to both sut and its registered listener
}
Thanks to #Jonathan for suggesting ArgumentCaptor, I can use it instead of my "re-invented wheel" ArgumentRetrievingAnswer. I managed to stub void methods for event subscribing to use ArgumentCaptor, although it has some after-taste of a hack.
ArgumentCaptor<OnEventListener> xyzViewTouchedListenerCaptor =
ArgumentCaptor.forClass(OnEventListener.class);
doNothing().when(xyzView).addViewTouchedListener(xyzViewTouchedListenerCaptor.capture());
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.
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());