Missing support for multiple browser tabs?

Does Vaadin support multiple web browser’s tabs? It doesn’t seem to. If you logon (or open) to same Vaadin application from two web browser tabs, you’ll get very soon either that famous red “out of sync error” or Vaadin updates same window status to both of browser tabs. Our test application has user security enabled (Springsource security) and it gives security key errors, because of this same problem. It seems that Vaadin (or maybe Ajax or GWT?) cannot distinguish two browser tab clients if they are using exactly same URL. There seems to be only one Vaadin server session for both tabs, which is incorrect.

Is there any way to solve this problem? Or is this “feature” and you simply cannot use browser tabs with same application? It’s bad thing if this is the case, because then we may have to discard Vaadin and find some other framework.

Multiple tab/window support is available, but you have to enable it explicitly by overriding Application.getWindow() method.

You can see it in action if you open the Sampler in two different tabs.

There’s lots of talk about this feature on the forums, and some add-ons try to make things easier by providing this automatically (namely Navigator and Navigator7, check the add-on
Directory
).

See this add-on:
http://vaadin.com/directory/#addon/navigator
- it makes implementing multi-tab support, bookmarking support, back-button support, etc. easy for your application.

Your examples of overriding Application.getWindow() didn’t help. Now I got only empty browser windows.

I added simple System.out.println(myApp.hashCode()) to start of application’s init() method. Output results shows clearly that Vaadin does not create new instance of my application for browser tabs. It creates new instance only for separate browser sessions.

My next question is how to get separate application instances for these browser tabs? Separating browser tabs sessions withing one instance of main application class is not fun. Is there some good reason why Vaadin won’t create new application instances for browser tabs by default?

Exactly. Each browser tab (or a separate browser window) is given a new window on the server-side - not a new application.

Yes. The logic in Vaadin is that there is one application per active user because those windows usually want to communicate with each other.

Whether you build all of your state to a class extending window or to a class extending application should not affect the complexity of the application.

Please see
Navigator example application

So Vaadin should have one application instance per active user. My intentention is to have two browser tabs pointing to same url, but having different users logged on. How can I do this, when Vaadin won’t create new application instance for second tab when I open it? Even if I could acceess my login window, those tabs would still use same application class instance, which is agains that statement that Vaadin has separate applications for diffrenet users.

You also wrote that:

I disagree that those windows would usually want to communicate with each other. More common case is that user wants to keep browser tabs strictly separate. For example if you open one Google search in one tab and another Google search on second tab, you want keep search results separate. I don’t understand when you would even want to open two broser tabs for one Vaadin application. It’s much more parctical to open new Vaadin window within same browser tab. If you do open new browser tab, you more likely want keep that new tab separate than connect it to existing application.

Also I don’t see how this multiple tabs and one application instance can work properly anyway. If application state is modified (f.ex. windows opened or closed) when one tab is active, the other browser tab doesn’t know anything about it. When user activates that other tab and tries to do something, there is out of sync situation as we have seen it so many times.

My opinion is that there cannot be working solution unless Vaadin creates separate applications for browser tabs. Could this behaviour be optional? For example so that you could call some static method like Application.supportMultiplebrowserTabs(true). If this feature is active, then Vaadin framework would use separate application instances for browser tabs. Otherwise it could work as it now does.

Two users using the same browser. This is not supported by Vaadin. May I guess that your use-case is that the person behind the browser is still the same, but he wants to log in as another user to do some admistration or give support?

Two solutions that I can think of

  • Advise the user to use two different browsers (for example firefox and safari)
  • Store the user id in your window object. The application instance would still be the same (as well as the HttpSession object is the same), but if all the user specific logic/state would be in the window, it would happily support multiple “users”.

If you have one window object per tab, there will be no out of sync errors. Furthermore - each window will know about those changes - they are transferred to client-side with the next ajax request (or immediately if polling or push is in use).

Admittedly - the current windowing system is fairly complicated and it will be revised in Vaadin 7. It is unlikely that we would drop 1 Application state per 1 HttpSession, because it is the common standard used by all stateful Java-based web applications (the state is stored in HttpSession), but supporting multi-window application will be easier.

Did you take a look of Navigator and the example app using it? It should be fairly easy to support multiple tabs with it without waiting for Vaadin 7.

Hi Joonas,

I’ve been trying to use the Navigator example with Vaadin 6.4.7 but there seems to be something fundamently wrong with it… The first hit on the server works as expected, but the second one simply has no response beyond initial rendering

  • Step 1 – Render my login page
  • Step 2 – Login
  • Step 3 – Browse the View
  • Step 4 – Fire a second browser tab
  • Step 5 – Render my login page a 2nd time
  • Step 6 – Login – Failure. Nothing happens.

It seems like the AJAX events never get interpreted by the server… But the code of vaadin itself is quite shamanic in nature, therefore quite hard to follow… It may be quite simple to fix, I just can’t find the source of the problem.

Can you reproduce the problem with Navigator demo application?

Not with the demo that you have online, but when i downloaded the code of the example (Found it around the forum) and ran the example using a simple jetty setup, I had a different issue.

I open 3 tabs of the ‘Editor’ tab, enter 3 different values and hit refresh on all 3 of them and I get an error message:

Here is my code for launching the example locally using Jetty:


package example;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.handler.HandlerList;
import org.mortbay.jetty.handler.ResourceHandler;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.servlet.Context;
import org.mortbay.jetty.servlet.HashSessionIdManager;
import org.mortbay.jetty.servlet.HashSessionManager;
import org.mortbay.jetty.servlet.ServletHolder;
import org.mortbay.jetty.servlet.SessionHandler;
import org.mortbay.resource.KnownValidJarFileResource;
import org.mortbay.resource.Resource;

public class SimpleExampleLauncher {
	
	public static void main( String[] args ) throws Exception {
		
		HashSessionIdManager idManager = new HashSessionIdManager();
		HashSessionManager sessionManager = new HashSessionManager();
		sessionManager.setIdManager( idManager );
		
		Server s = new Server();
		s.setStopAtShutdown( true );
		s.setSessionIdManager( idManager );
		
		// Connector 
		SelectChannelConnector connector = new SelectChannelConnector();
		connector.setAcceptors( 2 );
		connector.setPort( 8080 );
		s.addConnector( connector );
		
		// Handler Collection
		HandlerList container = new HandlerList();
		
		// Resource Handler
		ResourceHandler resourceHandler = new ResourceHandler() {
			
			private URL findURLResource( String path ) {
				if ( path.startsWith( "/" ) ) {
					path = path.substring( 1 );
				}
				
				if ( path.length() == 0 ) {
					return null;
				}
				
				// Try to find it stand-alone
				URL url = ClassLoader.getSystemClassLoader().getResource( path );
				if ( url != null ) {
					return url;
				}
				
				return null;
			}
			
			@Override
			public Resource getResource( String path ) throws MalformedURLException {
				if ( path == null || !path.startsWith( "/" ) ) {
					throw new MalformedURLException( path );
				}
				
				try {
					URL url = findURLResource( path );
					return newResource( url );
				} catch ( IOException ex ) {
					
					return null;
				}
			}
			
			/**
			 * Construct a resource from a url.
			 */
			public Resource newResource( URL url ) throws IOException {
				if ( url == null ) {
					return null;
				}
				
				String url_string = url.toExternalForm();
				
				if ( url_string.startsWith( "jar:file:" ) ) {
					return new KnownValidJarFileResource( url, Resource.getDefaultUseCaches() );
					
				} else {
					return Resource.newResource( url );
				}
			}
		};
		container.addHandler( resourceHandler );
		
		// Servlet Context
		Context context = new Context( container, "/", false, false );
		context.setSessionHandler( new SessionHandler( sessionManager ) );
		
		ServletHolder vaadinServletHolder = new ServletHolder();
		vaadinServletHolder.setName( com.vaadin.terminal.gwt.server.ApplicationServlet.class.getSimpleName() );
		vaadinServletHolder.setClassName( com.vaadin.terminal.gwt.server.ApplicationServlet.class.getName() );
		vaadinServletHolder.setDisplayName( com.vaadin.terminal.gwt.server.ApplicationServlet.class.getSimpleName() );
		vaadinServletHolder.setInitParameter( "application", SimpleExample.class.getName() );
		vaadinServletHolder.setInitParameter( "productionMode", Boolean.TRUE.toString() );
		context.addServlet( vaadinServletHolder, "/*" );
		
		s.addHandler( container );
		s.start();
		context.start();
	}
}

On what version of Vaadin does the online sample works? Perhaps I have using a version of vaadin that is too cutting edge (6.4.7, and planning to upgrade to 6.4.8)

Thanks in advance for any help you can provide.

Just checked (using the ?debug URL parameter), the demo is running on 6.2.2, so that is one likely reason you’re having trouble.

That’s what I thought… Nevertheless, I persisted, traced a lot of code and crafted a navigator-like approach for multi-tab support. It’s not finished yet (Still got to verify URL support with the UriFragmentUtility) but it looks promising.

I’ll keep you all posted :slight_smile:

Hey Joonas,

I’m doing my project in Vaadin 7. But, I still face this multiple tabs problem in it. The scenario is I will update a table from a server.
(say I’m adding a row in it)
.

When I open the same url in ‘n’ different tabs (or I refresh ‘n’ times). The table in the last opened (or refreshed) tab is updated ‘n’ number of times.
(I.e. ‘n’ no. of identical rows are added to the table in the last updated or opened tab)
Nothing happens on the other tabs.​

And I use ICEPush to update the UI.

Please help me in this!

Thanks,
Gugan

Could you, like i said in the other thread you made, post a bit of code showing how and where you’re adding the items to the table?

This is how I add an item to the table through container.

public static void updateTable(String message) {
IndexedContainer container = (IndexedContainer) table.getContainerDataSource();
container.addItem(container.size() + 1);
container.getContainerProperty(container.size(), "Column 1").setValue(message);
}

I’m guessing that your void doesn’t get called on the other users when the new UI is initialised.
You should call this void like
this
but instead of showing a notification like described in the wiki you should update the table/container.

Thank you Marius. Like you said in the other thread, I have made a static reference to the table. Now I modified my code and removed the static reference. Fixed it. Working fine.