Navigator with CDIView

Hi experts,

I’m playing with Vaadin (and loves it !) and am trying to inject EJBs in my CDIViews. I’m also using the cdi-mvp and cdi-properties addons.

I have my main UI class annotated with @CDIUI. I can inject beans and everything works fine.

My problem is I have some views annotated with @CDIView and every bean I tried to inject in is null.

The weird thing is the EJB injections works for the primary/main CDIView which doesn’t have name, but when I’m specifying a name like : CDIView(“/user”) the EJB doesn’t get injected and I got this error :

INFO: New BeanStoreContainer created
INFO: Attempting to retrieve view name from string "/user"
INFO: Looking for view with name ""
INFO: com.project.ui.view.impl.CalendarViewImpl is annotated, the viewName is ""
INFO: Bean Managed Bean [class com.project.ui.view.impl.CalendarViewImpl]
 with qualifiers [@Any @Default]
 with viewName "" is one alternative
INFO: com.project.ui.view.impl.UtilisateurViewImpl is annotated, the viewName is "/user"
SEVERE: java.lang.NullPointerException
    at org.vaadin.addon.cdimvp.ViewComponent.fireViewEvent(ViewComponent.java:19)
    at com.project.ui.view.impl.UtilisateurViewImpl.enter(UtilisateurViewImpl.java:97)
    at com.vaadin.navigator.Navigator.navigateTo(Navigator.java:571)
    at com.vaadin.navigator.Navigator.navigateTo(Navigator.java:526)
    at com.vaadin.ui.UI.doInit(UI.java:615)
    at com.vaadin.server.communication.UIInitHandler.getBrowserDetailsUI(UIInitHandler.java:223)
    at com.vaadin.server.communication.UIInitHandler.synchronizedHandleRequest(UIInitHandler.java:73)
    at com.vaadin.server.SynchronizedRequestHandler.handleRequest(SynchronizedRequestHandler.java:37)
    at com.vaadin.server.VaadinService.handleRequest(VaadinService.java:1371)
    at com.vaadin.server.VaadinServlet.service(VaadinServlet.java:238)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:770)
    at org.apache.catalina.core.StandardWrapper.service(StandardWrapper.java:1550)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:281)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:175)
    at org.apache.catalina.core.StandardPipeline.doInvoke(StandardPipeline.java:655)
    at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:595)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:161)
    at org.apache.catalina.connector.CoyoteAdapter.doService(CoyoteAdapter.java:331)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:231)
    at com.sun.enterprise.v3.services.impl.ContainerMapper$AdapterCallable.call(ContainerMapper.java:317)
    at com.sun.enterprise.v3.services.impl.ContainerMapper.service(ContainerMapper.java:195)
    at com.sun.grizzly.http.ProcessorTask.invokeAdapter(ProcessorTask.java:860)
    at com.sun.grizzly.http.ProcessorTask.doProcess(ProcessorTask.java:757)
    at com.sun.grizzly.http.ProcessorTask.process(ProcessorTask.java:1056)
    at com.sun.grizzly.http.DefaultProtocolFilter.execute(DefaultProtocolFilter.java:229)
    at com.sun.grizzly.DefaultProtocolChain.executeProtocolFilter(DefaultProtocolChain.java:137)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:104)
    at com.sun.grizzly.DefaultProtocolChain.execute(DefaultProtocolChain.java:90)
    at com.sun.grizzly.http.HttpProtocolChain.execute(HttpProtocolChain.java:79)
    at com.sun.grizzly.ProtocolChainContextTask.doCall(ProtocolChainContextTask.java:54)
    at com.sun.grizzly.SelectionKeyContextTask.call(SelectionKeyContextTask.java:59)
    at com.sun.grizzly.ContextTask.run(ContextTask.java:71)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.doWork(AbstractThreadPool.java:532)
    at com.sun.grizzly.util.AbstractThreadPool$Worker.run(AbstractThreadPool.java:513)
    at java.lang.Thread.run(Thread.java:662)

Here’s my code :

@Theme("mytheme")
@SuppressWarnings("serial")
@CDIUI
public class MyVaadinUI extends UI {
@WebServlet(value = "/*", asyncSupported = true, initParams = {
        @WebInitParam(name = "test", value = "com.project.ui.MyVaadinUI"),
        @WebInitParam(name = "UIProvider", value = "com.vaadin.cdi.CDIUIProvider")
    })
    @VaadinServletConfiguration(productionMode = false, ui = MyVaadinUI.class, widgetset = "com.project.AppWidgetSet")
    public static class Servlet extends VaadinServlet {
    }

    @Inject
    private CDIViewProvider cdiViewProvider;

    @Inject
    @HorizontalLayoutProperties(sizeFull = true)
    private HorizontalLayout hLayout;

    private HashMap<String, Class<? extends View>> routes = new HashMap<String, Class<? extends View>>() {
        {
// If I switch CalendarViewImpl and UtilisateurViewImpl
// and changes CDIView names, then CalendarViewImpl won't work anymore
// but UtilisateurViewImpl will
            put("", CalendarViewImpl.class);
            put("/user", UtilisateurViewImpl.class);
        }
    };

    private Navigator navigator;

    // Menu navigation button listener
    class ButtonListener implements Button.ClickListener {

        String menuitem;
        public ButtonListener(String menuitem) {
            this.menuitem = menuitem;
        }

        @Override
        public void buttonClick(ClickEvent event) {
            // Navigate to a specific state
            navigator.navigateTo("/" + menuitem);
            Notification.show(menuitem);
        }
    }


     @Override
    protected void init(VaadinRequest request) {
         setContent(hLayout);
         buildMenu();
         buildContent();
    }

    private void buildMenu(){
        // Have a menu on the left side of the screen
        Panel menu = new Panel("Menu");
        menu.setHeight("100%");
        menu.setWidth(null);
        VerticalLayout menuContent = new VerticalLayout();
        menuContent.addComponent(new Button("Calendar",
                  new ButtonListener("calendar")));
        menuContent.addComponent(new Button("Utilisateur",
                  new ButtonListener("user")));
        menuContent.setWidth(null);
        menuContent.setMargin(true);
        menu.setContent(menuContent);
        hLayout.addComponent(menu);
    }

    private void buildContent(){
        // A panel that contains a content area on right
        Panel panel = new Panel("An Equal");
        panel.setSizeFull();
        hLayout.addComponent(panel);
        hLayout.setExpandRatio(panel, 1.0f);
        navigator = new Navigator(this, panel);
        navigator.addProvider(cdiViewProvider);
       for (String route : routes.keySet()) {
            navigator.addView(route, routes.get(route));
        }
    }
public interface UtilisateurView extends MVPView{
// some methods
}
@UIScoped
@CDIView("/user")
public class UtilisateurViewImpl extends AbstractMVPView implements UtilisateurView, View{

    @Inject
    private Instance<UtilisateurList> utilisateurList; //always null if name specified with @CDIView

    @PostConstruct
    protected void initView(){
        //some code
    }

    @Override
    public void enter(){
        super.enter();
    }

    @Override
    public void enter(ViewChangeListener.ViewChangeEvent event) {
// Enter well here
        fireViewEvent(UtilisateurPresenter.REFRESH_LIST, null);
    }


}

CalendarViewImpl is basically the same as UtilisateurViewImpl without CDIView name specified.

Many thanks,

Regards

One thing you should probably not be doing is to add the views to the navigator manully. This will cause the Navigator to use the ClassBasedViewProvider and then no injection will take place… // REMOVE THIS for (String route : routes.keySet()) { navigator.addView(route, routes.get(route)); }
The CDIViewProvider will get the ‘route’/path of the view from the @CDIView annotation.

Thanks a lot for your help. I removed the code and I don’t get errors anymore.

But the navigation still doesn’t work, the unnamed view loads well, but when I want to change the view, nothing happens, and I got no errors.

Any ideas?

It does work !

I was doing a navigator.navigateTo( "/" + menuitem);
but the / is not needed, so menuitem only is enough.

I now can give all my CDIView a name, and I just added a

navigator.navigateTo("MyViewName");

at the end of my init method in my main UI, to have a default view. If not, the CDIViewProvider is automatically looking for a CDIView which has no name (“”) and an error can appears if all your CDIViews have a name.

Thanks a lot for your help.

Someone reported the same problem as me, but I think it was a misunderstood about the functionnality of CDIView : http://dev.vaadin.com/ticket/11951

I think that ticket is slightly different from your case. In his case ( if I read that correctly ) @CDIView did NOT work for him , but addView did , either vaadin-cdi was fixed since then , or he/she did something else wrong…

BTW you can easely extent CDIViewProvider to provide navigation to a default view other than with a name “” , you could even provide different default views depending on the user etc…

public class CdiNavViewProvider extends CDIViewProvider {

    @Override
    public String getViewName(final String viewAndParameters) {
        if ( (viewAndParameters == null) || viewAndParameters.isEmpty() ) {
            return super.getViewName("home"); // ..
        }
        return super.getViewName(viewAndParameters);
    }
}

Oh yes, you’re right about the ticket.

Good idea for extending the CDIViewProvider, I haven’t thought about that.

Thanks again :slight_smile: