Error with trying to store view state

Hi,

I am having an issue with the way my application loads views (my views all extend from Vaadin Layouts ie. MyBaseView extends VerticalLayout). The problem I am having is that I have 4 main screens in the app. I use a MenuBar to control navigation between the screens. Some of these screens can spawn a subscreen (which is just another view). When a new view is spawned I dynamically add a MenuItem to the corresponding MenuItem in the MenuBar which relates to the parent screen (the idea being that when I navigate away from the newly spawned view, I can get back to it by selecting the dynamically added MenuItem). I am trying to keep the MenuBar as a single instance for the application session and as such it is stored (instantiated and referenced) in my Application class. The MenuBar has a reference to all created views in the app and will either create a new view or load a cahced view depending on which MenuItem in the MenuBar is clicked. The problem I am having is that when I try to reload a cached view (all views are loaded using mainWindow.setContent(myView)), I get a warning from Vaadin and then the application gets out of sync.

WARNING [com.vaadin.terminal.gwt.server.AbstractCommunicationManager]
(http-localhost-127.0.0.1-8080-1) Warning: Ignoring variable change for non-existent component, VAR_PID=PID23

The PID23 relates to the MenuBar, according to the Vaadin debugger.

My question is, is it possible to reload a cached view in this way or am I doing something funadmentally wrong with the way I am tring to cache views. I would have though that seeing as the view (the server side pojo instance) is stored in the application class, and hence does not get GC’d, that I would be able to reload it?

So a MenuItem is selected and its listener you both modify the MenuBar and replace the contents of the mainWindow so that the same instance of MenuBar is moved into this new content? Could you post the code, how you are doing the handling.

Hey,

Apologies, I didn’t see your message earlier (I must not have marked notify by mail… :-/ ). I won’t have access to the code again until Monday but I did a smaller example from memory. It’s basically the same thing with less of the bulk of the actual code. The problem is reproducible with the below code.

Steps:
Load App
Click Add New
Try to click RTB in MenuBar (Nothing happens and if you click again you get the Out Of Sync error)

The sample reproduction code is below.
Like I mentioned previously it could well be some stupid that I’m doing but any guidance is much appreciated.

App Class:

public class MyApp extends Application {
	
	private static final long serialVersionUID = 1L;

	private static ThreadLocal<MyApp> sessionInstance = new ThreadLocal<MyApp>();

	Window mainWindow;
	public final Navigation nav;
	
	public MyApp() {
		sessionInstance.set(this);
		
		nav = new Navigation();
	}
	
	@Override
	public void init() {
		mainWindow = new Window("MenuItem Test");
		setMainWindow(mainWindow);
		
		nav.addMainView(new MyView());
	}

	public static void setCurrentView(IView view) {
		sessionInstance.get().mainWindow.setContent((VerticalLayout) view);
	}
	
	public static MyApp getInstance() {
		return sessionInstance.get();
	}
}

Navigation Bar

public class Navigation extends MenuBar {

	private static final long serialVersionUID = 1L;
	
	private MenuItem mainScreen, rtb;
	private Map<MenuItem, IView> viewCache = new LinkedHashMap<MenuItem, IView>();
	private int subCounter = 0;
	
	public Navigation() {
		setWidth("100%");
		
		mainScreen = this.addItem("Home Base", null);
		rtb = mainScreen.addItem("R.T.B.", myCmd);
		mainScreen.addSeparator();
	}
	
	public void addMainView(MyView view) {
		viewCache.put(rtb, view);
		
		MyApp.setCurrentView(view);
	}
	
	public void openView(IView view) {
		if (!viewCache.containsKey(view)) {
			viewCache.put(addNewSub(), view);
		}
		
		MyApp.setCurrentView(view);
	}
	
	public void closeView(MySubView view) {
		for (MenuItem menuItem : viewCache.keySet()) {
			if (viewCache.get(menuItem).equals(view)) {
				viewCache.remove(menuItem);
				removeSub(menuItem);
				break;
			}
		}
	}
	
	private MenuItem addNewSub() {
		return mainScreen.addItem("Screen " + ++subCounter, myCmd);
	}
	
	private void removeSub(MenuItem menuItem) {
		mainScreen.removeChild(menuItem);
	}
	
	
	
	Command myCmd = new Command() {

		private static final long serialVersionUID = 1L;

		@Override
		public void menuSelected(MenuItem selectedItem) {
			openView(viewCache.get(selectedItem));
		}
	};
}

Main View Class

public class MyView extends VerticalLayout implements Button.ClickListener, IView {

	private static final long serialVersionUID = 1L;
	
	public MyView() {
		HorizontalLayout header = new HorizontalLayout();
		
		addComponent(MyApp.getInstance().nav);
		
		addComponent(header);
		
		addComponent(new Label("Base"));
		
		Button add = new Button("Add New");
		add.addListener(this);
		addComponent(add);
	}

	@Override
	public void buttonClick(ClickEvent event) {
		MyApp.getInstance().nav.openView(new MySubView());
	}
}

Sub View

public class MySubView extends VerticalLayout implements Button.ClickListener, IView {

	private static final long serialVersionUID = 1L;
	
	public MySubView() {
		HorizontalLayout header = new HorizontalLayout();
		
		addComponent(MyApp.getInstance().nav);
		
		addComponent(header);
		
		addComponent(new Label("Sub"));
		
		Button deathToTheNonBelievers = new Button("Close Me");
		deathToTheNonBelievers.addListener(this);
		deathToTheNonBelievers.setImmediate(true);
		addComponent(deathToTheNonBelievers);
	}

	@Override
	public void buttonClick(ClickEvent event) {
		MyApp.getInstance().nav.closeView(this);
	}
}