UriFragmentUtility and initial URI used to enter the application.

Hi Guys,

[b]
Situation

[/b]
My Vaadin application uses a very classic sequence:

  1. Application.init
    1.1 new MyMainWindow()
    1.1.1 new UriFragmentUtility
    1.1.2 uriFragmentUtility.addListener( new MyFragmentChangedListener() )
    1.1.3 uriFragmentUtility.getFragment() == null
  2. MyFragmentChangedListener.fragmentChanged(…)

The UriFramentUtility.getFragment() javadoc says:

According to this comment, it seems normal that during 1.1.3, getFragment() returns null (because Application.init is not over yet). Still according the comment, my problem below seems known and “expected” :wink:


Problem

My problem is probably very common.

If the user enters through an URL with no anchor, it goes to the home page.
i.e.: http://www.MyDomainName.com/
At point 1.1.3, I decide to put in MyMainWindow, the content of the home page.
That case works fine.

But if the user enters through a more specific url, we directly show the corresponding content
i.e.: http://www.MyDomainName.com/#products
At point 1.1.3, we would decide not to show the home page in MyMainWindow, but the list of products.

But, because at point 1.1.3, uriFragmentUtility.getFragment() == null, MyMainWindow thinks it must show the home page. And the home page is briefly shown in the browser.
Immediately after, point 2 happens and the products page is constructed server side (content of MyMainWindow), then shown in the browser.


Question

How to avoid the 1st (useless) rendering of the home page?

I don’t understand the suggestion of the UriFramentUtility.getFragment() javadoc. Does it mean that I should look at the HttpServletRequest myself?
How would you solve that problem?


Further understanding

If I understand well, UriFramentUtility is mainly a browser side GWT component.
It means that if I’ve a new HttpSession, or even if I open a new tab, a first request/response cycle is needed to bring the component on the browser, and then a second request/response will notify the FragmentChangedListeners. It means that the current verison of UriFragmentUtility will not be of any help for the initial uri.

I think you’ve understood correctly the behaviour of URIFragmentUtility. And indeed, it is a client-side widget, and it doesn’t catch the initial fragment on the first request.

How about using plain-old URIHandler for your application to set the initial fragment? I’m not sure would it work any better but that’s what I’d try.

Hi!

The javadoc there is really bad there, shame on me. I have tried to lower the criticism by typing “experimental” on the class documentation :slight_smile:

I think I have mentioned TransactionListener there as a way to detect a one special case on app startup. But I gotta admit that I really don’t remember how it worked atm. It may be that it was a feature present on my prototype that finally didn’t even get to the released version.

In our Sampler application we each time render the home page first and then “forward” to linked view if fragment is detected. So there we are doing the extra server visit you mention :frowning:

For next major revision I hope we have time to merge uri fragment features to window and build it so that the initial fragment value is somehow present on init method.

PS. Jouni, fragment part of uri is not sent to server by default.

cheers,
matti

Hi!

Returning to the subject immediately. In [8496]
I added some new parameters to the initial UIDL request. I believe I had something similar on my prototype when the javadoc is written. Note that all those parameters are still totally unsupported. With next nightly you can catch the initial fragment with following code in init method (where u == UriFragmentUtility):

    getContext().addTransactionListener(new TransactionListener() {
        public void transactionEnd(Application application,
                Object transactionData) {
            // TODO Auto-generated method stub

        }

        public void transactionStart(Application application,
                Object transactionData) {
            HttpServletRequest r = (HttpServletRequest) transactionData;
            String parameter = r.getParameter("fr");
            if (parameter != null) {
                System.err.println("This is initial UIDL request. Nothing"
                        + " is rendered yet. Initial fragment value is "
                        + parameter);
                // make set the fragment value so that extra visit is not
                // required
                u.setFragment(parameter, false);

                // set the view according to the initial fragment value
                ...

            }
        }
    });

I hope this helps.

cheers,
matti

Thank you guys, I’ll try that.
John.

Hi Matti,

I just downloaded vaadin-6.1.0-pre2 and your solution works fine, thank you very much for your help.

In your post, you talk about another solution that would come (maybe) in 6.1. Do you have an update on that?

Many thanks.
John.

Hi!

UriFragment handling will not be merged into Windows in 6.1. We are also planning to split “subwindows” and “toplevel” windows, which are currently the same thing on server side although they really are very different beasts. I think we will do the UriFragment merge at the same time. I’d guess the version number will be 7, but I don’t want to promise anything about the schedule. We have quite a lot of things scheduled for this autumn already.

cheers,
matti

You have my vote for this, it would make things more clear indeed.

Don’t worry, the most important is that you help us to make things work, as you did for me.

Thank you Matti and keep on the good work.
John.

What it comes to splitting the class, I agree with you guys. To get more feedback on this I quickly
created an item to uservoice
. Anyone else feeling the same can vote for this.

Hi Matti,

The getParameter(“fr”) trick does not work as fine as I thought first.
It does not work for the initial request inside a new browser tab.

I’ve debugged and reverse engineered the com.vaadin.terminal.gwt.server.AbstractApplicationServlet for my understanding. I won’t go into the detail here.

Fundammentally, when I open a new browser tab and type the following URL:

http://localhost:8080/MyWebApp/#myScreenName

3 requests are sent:

  • 1. “normal” request (not UIDL).
  • 2. UIDL request with request.getParameter(“fr”) equals “myScreenName”
  • 3. UIDL request with request.getParameter(“fr”) == null, and fireing a FragmentChangedEvent in the FragmentChangeListener (with the fragment equals “myScreenName”).

My guess is that the browser does not send the anchor part (“#myScreenName”) of the URL (request 1).
See: http://stackoverflow.com/questions/774136/retrieving-anchor-link-in-url-for-asp-net/774270#774270
That’s probably the root of our problem.
So you (Vaadin) are forced to make JavaScript send the anchor explicitely to the server in an Ajax UIDL request (requests 2 and 3).

Thank to your trick (getParameter(“fr”), I can detect in the request 2 the anchor fragment (already better than request 3).
But I need it in the 1st request.

If it’s true that the browser does not send the anchor part of the url, I suggest the following:

  • Request 1: I instantiate a new application level window there (one per borwser tab). I should not select the screen yet, and rely on the knowlege that at least a second request will arrive.
  • Request 2: request.getParameter(“fr”) tells me the screen name and
    → f it’s blank (and the screen has never been set for that application window instance yet), I set the Home page screen.
    → else, if it’s not blank, I turn a flag in the application window, that we expect 3rd request that will fre a FragmentChangedEvent
  • Request 3: request.getParameter(“fr”) is null => I could beleive I’m in request 2, but hopefully, the flag in the application Window has been set (request 2) and we do nothing, because we know that FragmentChanged will be fired in a few mili seconds (than the right screen will be set).

Do you see a simpler way (except than waiting for the next version of your API that will probably provide a solution to that common problem), or am I right this time ?

Many thanks for sharing your knowlege.
John.

Hi!

I think you have got it right. I’ll try to explain it briefly.

Request 1:

  • normal http request
  • server detects application, window, theme etc and responds with a “normal” html page containing “kickstart” for the client side engine
  • uri fragment can’t be sent to server at this point (as per protocol)

Request 2:

  • initial XHR done by the client side engine
  • in this request we also send some extra parameters which can be resolved at this time: various size informations and the initial uri fragment (aka “fr” hack discussed above)
  • the server responses with UIDL message describing the initial state of the window

Request 3:

  • happens iff some component needs to update a variable to server. Like UriFragmentUtility has different value on client than on server or window is immediate and wants to update size
  • normal Vaadin client-server communication, does not contain “fr” parameter (nor any other general parameters, just Vaadin specific communication)

Your (and most likely many others too) problem is that you would like to do the actual main window initialization after request 2, instead of in Application.init() which is called way too early. When we will split top level windows and sub windows, we definitely need to have two phase initialization for top level windows. We just don’t have all the information ready when we need to instantiate windows.

cheers,
matti

Thank you for your answer, Matti,

I instantiate the first application level window in Application.init(). But when users open additional tabs, I’m figuring that out in Application.getWindow(name), and from there I create additional instance of the application level window, as discusses with Joonas. http://vaadin.com/forum/-/message_boards/message/49982

Yes, currently Window.init() does not exist. That would be great.

John.

Ok it works in my application now. Thank you again guys.

For the readers, here is what I’ve exactly done in MyApplication class.

In the transactionStart method, I’ve instered these 2 lines to remember the “fr” parameter.

		HttpServletRequest request = (HttpServletRequest) transactionData;
		veryInitialUriFragment.set(request.getParameter("fr"));  // Could be null.

In the constructor of the MainWindow class, I don’t decide of any screen yet (in the problematic version of my code the home page was set in MainWindow constructor) and I count on further immediate requests to tell me more (and make a choice).
Usually, that choice is made by the FragmentChangedListener (UriFragmentUtiliy is on MainWindow). But not for the home page, because there is no fragment (=> no FragmentChanged event).

In my application it’s the home page if the url has no “#” in the URL. (see prior post in the thread). I can detect if it’s a home page with the “fr” parameter begin equal to “” (not null, not anything else).
But, in the transactionStart method, it’s too soon: I don’t know yet which MainWindow instance is concerned (one instance per browser tab). As you see, in the transactionStart method above, I save the value of the “fr” parameter for later (in a ThreadLocal variable). And later is MyApplication.getWindow() (see other thread about multi tab browsing):

    		
    		// SEE: http://vaadin.com/forum/-/message_boards/message/57240
    		//   Probably to be removed with Vaadin 7 and the notion of application level window.
    		if ("".equals(getVeryInitialUriFragment())) { // This case is different from null. If "", then it's for the home page.
    			if (mainWindow.getCurrentScreen() == null) {  // screen not decided yet
    				mainWindow.initializeHomePageAsFristScreen();  // this tell the MainWindow to insert home page stuff (screen) between the header and footer.
    			}
    		}

Here is the complete code of MyApplication.getWindow(String name) for reference and further understanding.

	/** see ancestor useful JavaDoc */
    @SuppressWarnings("unchecked")
	@Override
    public Window getWindow(String name) {
    	// We check application state here because superclass returns null if
    	// a) the application is not running anymore
    	// b) it doesn't have a window for the given name
    	if (!isRunning()) {
    	    return null;
    	}
    	
    	Window result;
    	
        // Is that an existing window?
        Window existingWindow =  super.getWindow(name);
        if (existingWindow != null) {
        	result = existingWindow;
        	
        } else {  // New case where we create one instance of the main window per browser window/tab that the user opens in the same HttpSession (= in the same Application instance).
        	///// Is it a request for a new main window (from another browser tab, for the same user/session) ?
        	///// If the name is like MainWindowName_01234567890 (where the number is random), then it is a request for a new instance of main window.
        	
        	if  (! isNameOfAMainWindow(name)) {
        		return null;
        	}
        	
            /////// We create a new instance of a main window.
        	MainWindow additionalMainWindow = new MainWindow();
        	additionalMainWindow.setName(name);  // we could leave the name null and it would be set to a random number "012345679", but the form "MainWindowName_0123456789" is probably clearer when debugging.
        	addWindow(additionalMainWindow);
        	result = additionalMainWindow;
        }
        
    	if  (isNameOfAMainWindow(name)) {
    		MainWindow mainWindow = (MainWindow)result;
    		currentMainWindow.set(mainWindow);  // ThreadLocal useful in other parts of the application.
    		
    		// SEE: http://vaadin.com/forum/-/message_boards/message/57240
    		//   Probably to be removed with Vaadin 7 and the notion of application level window.
    		if ("".equals(getVeryInitialUriFragment())) { // This case is different from null. If "", then it's for the home page.
    			if (mainWindow.getCurrentScreen() == null) {  // screen not decided yet
    				mainWindow.initializeHomePageAsFristScreen();
    			}
    		}
    	}

    	return result;
    }	
	/** Is name like MainWindowName_01234567890   ?? */
	private boolean isNameOfAMainWindow(String name) {
    	Window primaryMainWindow = getMainWindow();
    	if (primaryMainWindow == null) {
    		// We may be in a strange case where Application.getWindow() is called before Application.setMainWindow() has been called. Would it be a bug?
    		return false;
    	}
    	String[] nameParts = name.split("_");
    	return name.equals(primaryMainWindow.getName()) ||
    		  (nameParts.length == 2  &&  nameParts[0]
.equals(primaryMainWindow.getName()));
	}

Hi Joonas,

I have found no support for this problem into the Navigator component.

In Navigator.moveTo() method, add these this little sleep:

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

Let’s say that these 3 seconds simulate some DB calls and complex client side layout arranging. It make the problem appear clearly. Without the sleep, your demo apps is too fast and it’s less obvious to notice the problem, while it may be doable.

Steps to reproduce the problem:

Do you want that I add the necessary stuff into the Navigator component to change this behaviour?

Kind regards.
John.

I did not fully understand what is the problem. How it shows?

As simple as that, Joonas:

The problem is that the dashboard is not supposed to be even created.

Now why does this happen… that’s the purpose of the posts from this thread which explains it quite well IMO.

Someone else may be able to say more about this, but how this happens AFAIK:

The UriFragmentUtility is a component that is added on a page. Without support from this client-side component, the server never sees the part of the URI after the hash.

When you load a page, the server can only serve the main page as it does not know the fragment you want to navigate to. Then, when the utility is “shown” on the browser, it obtains the URI fragment and sends it to the server, which can decide to replace the current page content with another view. However, at this time, also the other components on the main page are shown to the user.

Two ways to resolve this would be to move the UriFragmentUtility functionality deeper into the client side framework or to use a main page that only contains the UriFragmentUtility and by default redirects the user to a real “main page”, e.g. to #main, unless a fragment is given.

Henri is right. The only (and correct) way to resolve this issue is to embed uri fragment utility in the framework.

Thank you Henri for your reply.

Certainly a must-have for Vaadin 7, if it want to simplify the window programming model for the web-page oriented developper.

The problem is that if no fragment is given, the UriFragmentUtiligy fires no event. We have to use the “fr” hack described in this thread.

Once Joonas will have read the whole thread and that I’ll have his agreement, I’ll implement that in the Navigator.

Not sure why the #Fragment scheme was chosen over regular URL params. GWT does this too, but as mentioned before, the server doesn’t get #Fragment code sent to it by a web browser, but it would if URL params were used.

Perhaps when more complex fragment needs may make this approach better, but for most of us who just want to know view names and possibly another param to identify the object the view is working on, URL params would work more cleanly for server coding.