When do constructors and enter() get called?

I have an application that I recently re-wrote for Vaadin 7 and it is working fine. I’m using the Navigator to go from my init() routine to a login page, and from there to my main application view. No problems so far…

Now I need to modify the program behavior a bit and it is giving me some grief. I think I don’t understand the inner workings of the system well enough - perhaps someone can explain it to me or point me at the appropriate documentation.

The new situation is this. Normally, a user logs in to my program and then selects a set of graphs to display. Now, I want to allow someone to put the name of the graph playlist on the URL line as a parameter. When this happens, I will log the user in as ‘anonymous’, thereby limiting their access to some program features, and then display the playlist they want to see.

So in my init() routine, I pick up the playlist info, login the user as ‘anonymous’, skip the login screen and navigateTo(“displayPage”). This also seems to work. However, it appears that the constructor for my display page is called multiple times, as is the displayPage::enter() routine. When I go through the login page, it appears that the displayPage.enter() is called only once. Since I build a toolbar in the enter() routine, it shows up twice on the page…

So I’m confused - how does the enter() routine get called, and what triggers the constructor call for that object? I am only calling the navigate routine once (that I’m aware of - although I’m trying to see if maybe I have a loop somewhere…)

Any hints would be welcome

thanks,

nbc

There’s a bug/inefficiency in the Vaadin 7.0 Navigator that ViewProvider.getView() is called twice when navigating. This may end up unnecessarily creating a View instance. The issue has been fixed in 7.1. However, calling enter() twice sounds strange. Could you see if you can come up with a minimal test case exhibiting the problem?

I’m trying to come up with a simple example, and as I’m doing that, I’ve found an anomaly that
makes me wonder if I’m using the Navigator incorrectly… My code looks more or less like this:


       // My login view class...
    public LoginView(){
        Logger.info("Start Login screen...");
    }

    public enter(){
        Logger.info("LoginView:: enter() called");
       ....
       ....
    }

       // main program
    public void init(VaadinRequest request){
        initProperties();
        ....
        ....
        navigator = new Navigator(this, rootPanel);
        navigator.addView("LoginView", LoginView.class);
        navigator.addView("DispatcherView", DispatcherView.class);
        ....
        ....
        navLocation = processParameters(request);
        Logger.info("INIT: Process Param complete - navigate to: " + navLocation);  // simple case returns "LoginView"
        navigator.navigateTo(navLocation);
        Logger.info("INIT: NavigateTo completed...");
    }

    When I run my program, the output is:

    INIT: Process Param complete - navigate to: LoginView
    Start Login Screen...
    LoginView::enter() called
    INIT: NavigateTo completed...
    Start Login Screen...
    LoginView::enter() called

So it looks like the LoginView constructor (and enter()) is called twice - why? Am I setting this up incorrectly?
The Login page displays correctly (shows a text field for username and password with a login button).

If this isn’t enough information, I will try to create a complete program that is stripped down to just this section.
So far I have just been adding print statements to my main code…

Thanks in advance,

nbc

Ok - so here is a complete program that indicates that I don’t have a clue what I’m doing :slight_smile:

The program is supposed to load a login view which contains a couple of text fields and a button.

Note the createNavigator() routine. If I use the line: navigator.addView(VN_LOGIN, LoginView.class);
then the constructor is called twice, as is the enter() routine (see the output below). However, the
display shows only one set of textfields and the button. If I use the commented line
(navigator.addView(VN_LOGIN, new LoginView()); then the constructor is called once, but TWO sets
of input fields and buttons are displayed on the page. But I’m only calling the navigator routine
one time (that I’m aware of). So what am I doing wrong?


The output from running the program in Tomcat is (using navigator.addView(VN_LOGIN, LoginView.class):
INFO: Server startup in 1388 ms
Jun 14, 2013 4:53:36 PM com.vaadin.server.DefaultDeploymentConfiguration checkProductionMode
WARNING: 
=================================================================
Vaadin is running in DEBUG MODE.
Add productionMode=true to web.xml to disable debug features.
To show debug window, add ?debug to your application URL.
=================================================================
INIT: Process PARAMETERS Complete - navigate to LoginView
Start Login Screen...
LoginView:: enter() called
Leave LoginView::enter
INIT: NavigateTo completed...
Start Login Screen...
LoginView:: enter() called
Leave LoginView::enter

    *** One instance of username, password and login button is displayed. Looks like constructor is called twice.

The output from running the program in Tomcat is (using navigator.addView(VN_LOGIN, new LoginView()):

INFO: Server startup in 1340 ms
Jun 14, 2013 4:55:52 PM com.vaadin.server.DefaultDeploymentConfiguration checkProductionMode
WARNING: 
=================================================================
Vaadin is running in DEBUG MODE.
Add productionMode=true to web.xml to disable debug features.
To show debug window, add ?debug to your application URL.
=================================================================
Start Login Screen...
INIT: Process PARAMETERS Complete - navigate to LoginView
LoginView:: enter() called
Leave LoginView::enter
INIT: NavigateTo completed...
LoginView:: enter() called
Leave LoginView::enter

    *** TWO instances of username/password/button show up on screen. Constructor only called once, enter() twice...

Here is the program:

package com.test.nbc;

import com.vaadin.navigator.Navigator;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.Panel;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.verisign.common.utils.VRSNLogger;
import com.test.nbc.views.LoginView;

/**
 * Main UI class
 */
@SuppressWarnings("serial")
public class JTestUI extends UI {
	private static final VRSNLogger Logger = VRSNLogger.getLogger(JTestUI.class);

	public static final String VN_DISPATCHER = "DispatcherView";
	public static final String VN_LOGIN = "LoginView";

	private Navigator navigator = null;
	private Panel rootPanel = null;
	private String navLocation = null;
	private VerticalLayout vl_ToolBar = null;
	private VerticalLayout rootLayout = null;
	@Override
	protected void init(VaadinRequest request) {
		// Create new layout
		rootLayout = new VerticalLayout();
		rootLayout.setSizeFull();

		rootPanel = new Panel();
		rootPanel.setSizeFull();

		createNavigator();
		
		vl_ToolBar = new VerticalLayout();
		rootLayout.addComponent(vl_ToolBar);
		rootLayout.addComponent(rootPanel);
		rootLayout.setExpandRatio(rootPanel, 1.0f);
		setContent(rootLayout);

        navLocation = processParameters(request);
        Logger.info("INIT: Process PARAMETERS Complete - navigate to " + navLocation);

		navigator.navigateTo(navLocation);
        Logger.info("INIT: NavigateTo completed...");
	}

	private void createNavigator(){
		navigator = new Navigator(this, rootPanel);

		navigator.addView(VN_LOGIN, LoginView.class);
//		navigator.addView(VN_LOGIN, new LoginView());
//		navigator.addView(VN_DISPATCHER, DispatcherView.class);
	}

    private String processParameters(VaadinRequest request){
        String navLoc = VN_LOGIN;

        return(navLoc);
    }
}

package com.test.nbc.views;

import com.vaadin.event.ShortcutAction.KeyCode;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent;
import com.vaadin.ui.Alignment;
import com.vaadin.ui.Button;
import com.vaadin.ui.Button.ClickEvent;
import com.vaadin.ui.Label;
import com.vaadin.ui.NativeButton;
import com.vaadin.ui.Panel;
import com.vaadin.ui.PasswordField;
import com.vaadin.ui.TextField;
import com.vaadin.ui.UI;
import com.vaadin.ui.VerticalLayout;
import com.verisign.common.utils.VRSNLogger;
import com.nbc.test.JTestUI;

public class LoginView extends VerticalLayout implements View {
	private static final long serialVersionUID = 1L;

	private static final VRSNLogger Logger = VRSNLogger.getLogger(LoginView.class);

	private Button bLogin = null;
	private JTestUI myUI = null;
	private PasswordField txt_Password = null;
	private TextField txt_UserName = null;

	public LoginView(){
		Logger.info("Start Login Screen...");
		myUI = (JTestUI) UI.getCurrent();
	}

	@Override
	public void enter(ViewChangeEvent event) {
        Logger.info("LoginView:: enter() called");
		setSizeFull();
		setSpacing(true);

		Label l_pgmTitle = new Label("Test Pgm");
		l_pgmTitle.setHeight("128px");
		l_pgmTitle.setStyleName("vrsn-header-Title");
		
		Panel p = new Panel("Enter Username and Password to log in");
		p.setWidth("500px");
		p.setHeight("256px");
		VerticalLayout vl_Login = new VerticalLayout();

		txt_UserName = new TextField();
		txt_UserName.setCaption("Username:");
		txt_UserName.focus();

		txt_Password = new PasswordField();
		txt_Password.setCaption("Password:");

		bLogin = new NativeButton("Login");
		bLogin.setStyleName("vrsn-dispatch-button");
		bLogin.setClickShortcut(KeyCode.ENTER);

		bLogin.addClickListener(new Button.ClickListener() {
			private static final long serialVersionUID = 1L;

			@Override
			public void buttonClick(ClickEvent event) {
				boolean success = false;

				success = doLogin();
				if(success){
					myUI.getNavigator().navigateTo(JTestUI.VN_DISPATCHER);
				}
			}
		});

		txt_UserName.setTabIndex(1);
		txt_Password.setTabIndex(2);
		bLogin.setTabIndex(3);

		vl_Login.addComponent(txt_UserName);
		vl_Login.addComponent(txt_Password);
		vl_Login.addComponent(bLogin);
		vl_Login.setComponentAlignment(txt_UserName, Alignment.MIDDLE_CENTER);
		vl_Login.setComponentAlignment(txt_Password, Alignment.MIDDLE_CENTER);
		vl_Login.setComponentAlignment(bLogin, Alignment.MIDDLE_CENTER);

		p.setContent(vl_Login);

		addComponent(l_pgmTitle);
		addComponent(p);
		setComponentAlignment(l_pgmTitle, Alignment.MIDDLE_CENTER);
		setComponentAlignment(p, Alignment.MIDDLE_CENTER);

        Logger.info("Leave LoginView::enter");
	}

	private boolean doLogin(){
            String username = txt_UserName.getValue();
            boolean retval = false;

            Logger.info("User: " + username + " Testing Navigation - Login always fails...");

            return(retval);
        }
}

Ok - Progress of sorts… It looks like the navigator gets called automatically after the init() routine is called. So I call navigateTo(Login) inside init() and that sets the navigator state to Login, calls my LoginView and then calls it again after leaving init(). So that is where the duplicate calls are coming from.

I see a method called Navigator.getState() which tells me where the Navigator wants to go. But how do I set the state? I don’t see a corresponding setState() method… There appears to be one inside the NavigatorManager, but that is protected and not accessible to me - I’m missing something here - what is it? Where does the code go after the init() routine is called? I’m assuming I should put my call to navigateTo() in a routine AFTER init(), but I’m not sure what that is…

Thanks,

nbc

I found the same issue in code from the Vaadin 7 training class that I took in Chicago a few months ago. The code is called ExampleApp and creates a Navigator with 3 views (dashboard, accounting and department). The code navigates to the dashboard in the init() routine, and then again as soon as init() finishes. If you take out the first call to navigateTo(), then it throws an exception because the navigator has no valid state set.

This duplicate calling sequence is causing me some real problems. I’d like to know if someone can suggest a way for me to work around it…

thanks,

nbc

Ahh, I see. After UI init, the navigateTo() method is called to ensure the navigator is in the correct state even if it was not done in init(). This is a bit unoptimal; there should perhaps be a check to see if it’s actually necessary. However, in general, instead of “redirecting” in init() by calling navigateTo(someViewName), there should preferably be a “root” or “default” view mapping with an empty string as the view name. This view is shown when the user enters the root URL of your application.

Also, unconditionally doing navigateTo() in init() will break URLs pointing to other views - the user will always be redirected to a view they didn’t mean to. Sometimes this is a valid use case, of course, such as when showing a login view if the user was not already logged in. In that case, the original URL should be stored and shown once the user has logged in.

The actual problem is with your enter() implementation. The enter() method might be called an arbitrary number of times during the application lifespan - for instance, think what happens if the user enters a view, then goes to another view, then comes back to the original. Your view class should either ensure the view content is built only once, or clear and rebuild it each time the view is entered. You can also register a view class instead of instance with addView() - in this case the framework creates a new instance of the view each time it’s entered.

I see what you are saying, and in my case, I am registering a view class, not an instance, so that isn’t a problem for me at the moment. I will have to look into creating a view with an empty name to start with - but I’m still not sure what that buys me. I want to have the init() routine decide whether to go to my login page or to log the user in as an ‘anonymous’ user and then go directly to my program view page - but when I make that decision, I don’t seem to have any way to set the view state inside of init() without calling navigateTo() - and if I do, then it gets called twice.

I have found a workaround - but I don’t like it… I can skip the navigateTo() call in init() and instead set the errorView to the view that I want to transfer to. When init returns, it calls navigateTo(), doesn’t find anything set and then jumps to the error view. Ugly, but for the moment it is doing what I want…

It would be nice to have access to the setState() method in the navigator so I could set the state and transfer to it later. Or have an option that would tell the Vaadin code not to call navigateTo after init if I have already done so…

nbc

Hi, this is somewhat dirty workaround but since the doInit calls navigator.navigateTo always after init(request), you could try to create your own state manager and pass the state to it in the init to bypass the actual events related to navigation. This way the navigateTo will forward to the selected view after the init is complete.


public void init(VaadinRequest request){
        ....
        Navigator.UrifragmentManager stateManager = new com.vaadin.navigator.Navigator.UriFragmentManager(this.getPage());
        navigator = new Navigator(this, stateManager, new Navigator.SingleComponentContainerViewDisplay(rootPanel));
        navigator.addView("LoginView", LoginView.class);
        navigator.addView("DispatcherView", DispatcherView.class);
        ....
        navLocation = processParameters(request);
        stateManager.setState(navLocation);
    }

Works for me but I’ve done it only with Scala (using Scaladin) so the Java code is just quickly written pseudo, I didn’t try to compile or run it.

Hi Matti,

That looks like it might work - I’ll give it a try. But I’m still not sure why that is necessary - why is the setState() call protected for the default stateManager? If that was visible, I could just set the state directly instead of having to create my own stateManager object…

Could it work with the following:


public class DemoNavigator extends Navigator {
    public DemoNavigator(UI ui, ComponentContainer container, String initialState) {
        super(ui, container);
        getStateManager().setState(initialState);
    }
}
...
public void init(VaadinRequest request){
    navLocation = processParameters(request);
    ....
    navigator = new DemoNavigator(this, rootPanel, navLocation);
    navigator.addView("LoginView", LoginView.class);
    navigator.addView("DispatcherView", DispatcherView.class);
    ....  
}

That would be a lot cleaner implementation. I somehow forgot about the protected getStateManager method. I was going to create a new ticket few months back for Vaadin about making it public by default but somehow the ticket never created itself…

That would be cleaner - I’ll see if I can make that work

thanks,

nbc

Hi,

I have following the same example (by extending the navigator) but I still get my enter() method called twice.

Any hint to solve this issue ??

I have the same problem with the duplicate navigateTo methods. It’s causing me all kinds of problems in trying to do authentication and dynamic loading. It’s very annoying!

I have the same problem…

some hint please.

+1

So, the problem is that UI.doInit, which calls our init, ends with:

    [b]
init(request);
[/b]

    Navigator navigator = getNavigator();
    if (navigator == null)
        return;
    [b]
navigator.navigateTo(navigator.getState());
[/b]
}

Ie, if you did a “navigateTo” in your init, doInit will do it again.
The solution, as suggested above, is to set state instead of calling navigateTo in your init.
To do this I created my own subclass of Navigator:

    static class MyNavigator extends Navigator {
    
        public MyNavigator(UI ui, SingleComponentContainer container) {
            super(ui, container);
        }
        
        public void setState(String navigationState) {
            getStateManager().setState(navigationState);
        }

    }

And then:

    public void init(VaadinRequest request) {
  ...
        navigator = new MyNavigator(this, mainComponent.screenContainer);
  ...
        navigator.setState(<the state I want>);
    }

Thanks for the tip ! I was facing te same problem with the enter() called twice because of my navigator.navigateTo(…) in the UI init method.

This is not working for me. I have the same exact implementation as Guttorm Vik, but for some reason the view is still entered two times. Any idea of where is this happening? I don’t seem to find it. One time is in UI.doInit(), but that’s accessed just once.

We also have the problem of twice initialization:

@SpringView(name = ImportView.VIEWNAME)
public class SomeView extends VerticalLayout {
  public final static String VIEWNAME = "someView";

  private boolean initialized = false;

  @Override
  public void enter(ViewChangeListener.ViewChangeEvent event) {
    // Because enter is called twice
    if (initialized) {
      return;
    }

    this.setMargin(false);
    this.setSizeFull();
    this.setSpacing(false);
    this.addComponent(VaadinElementFactory.createHorizontalBarWithLogoutButton(getUI()));
    initialized = true;

  }
}

The start point is a class extending the UI which has a method overwritung the init method and calling this: getNavigator().navigateTo(ImportView.VIEWNAME);
Is there some way to avoid this? We are using Vaadin 7.5