URL parameters at application/window initialization

Hello!

I am trying to build one of the simplest applications you can imagine. In short, it should do the following:

  1. At startup, recieve a file ID and some other parameters via the URL. For example ?fileId=myFile.png&foo=bar
  2. If the named file already exists on disk, display the contents of the file (in this case an image).
  3. If the named file does not already exist on disk, display an upload box for the file (.

This is all very simple, and I have a clear idea of how I would proceed in doing it. But, I ran into problems with the ParameterHandler – It seems to do its processing in handlerParameters()
after
the Application’s init() is done, meaning the parameters are not available when I need them at application startup. Below is a simple example.

Application:


package com.example.parameterhandlerapplication;

import com.vaadin.Application;
import com.vaadin.ui.*;

@SuppressWarnings("serial")
public class ParameterhandlerapplicationApplication extends Application {
	@Override
	public void init() {
		Window mainWindow = new Window("Parameterhandlerapplication Application");

		MyParameterHandler paramHandler = new MyParameterHandler();
		mainWindow.addParameterHandler(paramHandler);

		Label label = new Label("fileId: " + paramHandler.getFileId());
		mainWindow.addComponent(label);

		setMainWindow(mainWindow);
	}
}

MyParameterHandler:


package com.example.parameterhandlerapplication;

import com.vaadin.terminal.ParameterHandler;
import java.util.Map;

@SuppressWarnings("serial")
public class MyParameterHandler implements ParameterHandler {
	String fileId = "";

	public void handleParameters(Map parameters) {
		System.out.println("found: " + parameters.containsKey("fileId"));
		if(parameters.containsKey("fileId")) {
			fileId = ((String[]) parameters.get("fileId"))[0]
;
		}
		System.out.println("end: " + this.fileId);
	}

	public void setFileId(String fileId) {
		this.fileId = fileId;
	}

	public String getFileId() {
		System.out.println("get: " + this.fileId);
		return this.fileId;
	}
}

Output when requesting via URL ‘http://localhost:8080/ParameterHandlerApplication/?restartApplication&fileId=test’:


get: 
found: true
end: test

As you can see in the output, the fileId property in MyParameterHandler hasn’t been updated at the time that init() calls getFileId() to retrieve the value of the parameter.

Now, I know it’s possible to put the code for displaying the appropriate GUI components inside handleParameters() instead of init(), but doing so feels like a non-clean way to write this code. As I see it, setting up the initial GUI should be done in the Application, not inside the parameter handler.
Indeed, I could probably put handleParameters() in the Application, but this would still be a “second phase” method that is run after the application is started, to “make things right”.
I would prefer to set up my GUI once and in one place, instead of partially in the “main” place and then having a second component adjust it, especially since this is at the very basic level of the GUI.

Seeing as one of the most common uses of URL parameters is to base decisions on their contents, am I wrong in expecting the parameters to be available at the time of setting up my GUI?

And of course, the obvious question; Is there a way to be able to access the url parameters at the time of init()? If it’s not possible using ParameterHandler, can I access them by some other means?

PS: I have been reading though all the posts I could find in the forum, but didn’t feel there is a clear answer to this, so I’m hoping this thread will be the one for that :slight_smile:

The first thing I noticed was that you do not take into account that Vaadin is stateful. For example, if you first browse to the url with the parameter fileId=foobar.png and if the file exists, you will show the image to the user. If you now change the parameter to fileId=foobar2.png (assuming that file doesn’t exist), you will still be showing the user the foobar.png file! This is because Vaadin’s state was from the first request, since init won’t be called the second time your request the application. What you actually should do, is to create the view based on what the parameter is and update the main layout accordingly.

Here’s a suggestion for implementing your application. Take a look at the
Vaadin application foundation
library, especially the
view module
. Create three classes, your application class, one view class for the showing the existing file and one view class for showing the upload component. The stubs for your views should look something like this


public class ShowExistingFileView extends AbstractView<VerticalLayout> {
    // Implement this class accordingly to the view module's documentation
}

public class UploadFileView extends AbstractView<VerticalLayout> {
    // Implement this class accordingly to the view module's documentation
}

And all you need to do in your application class is this:


class YourApplication extends Application implements ParameterHandler, ViewContainer {

    private AbstractView<?> currentView = null;
    private VerticalLayout mainLayout = new VerticalLayout();

    @Override
    public void init() {
        getContext().addTransactionListener(new ViewHandler(this));
        Window mainWindow = new Window("Parameterhandlerapplication Application");

        mainWindow.addParameterHandler(this);

        mainWindow.addComponent(mainLayout);
        // Register child views
        ViewHandler.addView(ShowExistingFileView.class, this);
        ViewHandler.addView(UploadFileView.class, this);
    
        setMainWindow(mainWindow);
    }

    public void activate(AbstractView<?> view) {
        if(currentView == null) {
             mainLayout.addComponent(view);
        } else {
            // Activate the view by replacing the current view with the given view in 
            // the main layout
            mainLayout.replaceComponent(currentView, view);
        }
        currentView = view;
    }

    public void handleParameters(Map parameters) {
        if(parameters.containsKey("fileId")) {
            fileId = ((String[]) parameters.get("fileId"))[0]
;
            // Does the file exist?
            if(fileIdExists(fileId)) {
                // File exists, activate the show file view, give the file
                // id as the parameter
                ViewHandler.activateView(ShowExistingFileView.class, fileId);
            } else {
                // File didn't exist, show the upload view
                ViewHandler.activateView(UploadFileView.class);
            }
        }
    }
}

The view handler will tell your application to change its view accordingly to which file id is passed as parameter. This approach will also handle parameter changes within the same vaadin session. What you also could do, is when you’ve uploaded a file in the upload view, you could activate the ShowExistingFileView and give as the parameter the newly uploaded file’s name.

I second the concern posted by the original poster. It seems like the sequencing of the application initalization would benefit from the URL parameters being passed and have the ParameterHandler.handleParameters() code be called before the Application.init() is called. This gives the application enough chance to do the initialziation based on URL parameters if needed.

We are seeing the same issue affecting our application (built using 6.8.9)

Is there a different way to achive this so that we can have our Application.init() be able to read the URL parameters passed?

  • Narinder.