Applications typically need to access some objects from practically all user interface code, such as a user object, a business data model, or a database connection. This data is typically initialized and managed in the application class. Some such data is built-in into the Application class, such as the locale.

You can access the application object from any user interface component using the getApplication() method. For example:

class MyApplication extends Application {
    UserData userData;

    public void init() {
        userData = new UserData();
    }

    public UserData getUserData() {
        return userData;
    }
}

...
data = ((MyApplication)component.getApplication()).getUserData();

The ThreadLocal pattern gives a solution to the global access problem by solving two sub-problems.

As the first problem, the servlet container processes requests for many users (sessions) sequentially, so if a static variable is set in a request belonging one user, it could be read or re-set by the next incoming request belonging to another user. This can be solved by setting the global reference at the beginning of each HTTP request to point to data of the current user, as illustrated in Figure 12.11.


You can implement such switching either with the TransactionListener or HttpServletRequestListener interface by setting the reference in transactionStart() or onRequestStart(), respectively. We use the former interface in the example code in this section, as the latter interface has to be implemented in the application class.

The second problem is that servlet containers typically do thread pooling with multiple worker threads that process requests. Therefore, setting a static reference would change it in all threads running concurrently, possibly just when another thread is processing a request for another user. The solution is to store the reference in a thread-local variable instead of a static. You can do so by using the ThreadLocal class in Java for the switch reference.


Notice that if you use a TransactionListener, the listeners are attached to the web application context (in practice a user session), not the application instance. The problem is that an application context can have multiple different Vaadin applications that share the same user session. If two of these applications add a transaction listener to the context to listen for requests, both are called and without any checks they would both set the reference to themselves. Therefore, the application data object needs to know which application it belongs to and check that when the transaction begins and ends. Using the HttpServletRequestListener frees you from these checks.

While you may not absolutely need to clear the reference in transactionEnd(), you are probably on the safer side if you do. Setting such unneeded references to null can help avoid memory leaks and it could also be a good security precaution not to leave a reference to session data so that it could be seen by another user session in the next request.

We end up with the following code. As we put the application data to a class separate from the application class, we have to make it a TransactionListener.

/** Holds data for one user session. */
public class AppData
       implements TransactionListener, Serializable {
    private ResourceBundle bundle;
    private Locale locale;   // Current locale
    private String userData; // Trivial data model for the user
    
    private Application app; // For distinguishing between apps

    private static ThreadLocal<AppData> instance =
        new ThreadLocal<AppData>();
    
    public AppData(Application app) {
        this.app = app;

        // It's usable from now on in the current request
        instance.set(this);
    }

    @Override
    public void transactionStart(Application application,
                                 Object transactionData) {
        // Set this data instance of this application
        // as the one active in the current thread. 
        if (this.app == application)
            instance.set(this);
    }

    @Override
    public void transactionEnd(Application application,
                               Object transactionData) {
        // Clear the reference to avoid potential problems
        if (this.app == application)
            instance.set(null);
    }

    public static void initLocale(Locale locale,
                                  String bundleName) {
        instance.get().locale = locale;
        instance.get().bundle =
            ResourceBundle.getBundle(bundleName, locale);
    }
    
    public static Locale getLocale() {
        return instance.get().locale;
    }

    public static String getMessage(String msgId) {
        return instance.get().bundle.getString(msgId);
    }

    public static String getUserData() {
        return instance.get().userData;
    }

    public static void setUserData(String userData) {
        instance.get().userData = userData;
    }
}

We can then use it in the application as follows. Observe that we do not have a reference to the application object in the constructor of the MyComponent class.

/** 
 * We can now nicely access the session-global data
 * in the constuctor of this class.
 */
class MyComponent extends CustomComponent {
    public MyComponent() {
        VerticalLayout layout = new VerticalLayout();
        
        // Get stuff from the application data object            
        layout.addComponent(new Label("Hello, " +
            AppData.getUserData()));

        layout.addComponent(new Label("Your locale is " +
            AppData.getLocale().getDisplayLanguage()));
        
        layout.addComponent(new Button(
            AppData.getMessage(MyAppCaptions.CancelKey)));
        
        setCompositionRoot(layout);
    }
}

/** The application class. */
public class ThreadLocalApplication extends Application {
    public void init() { 
        Window main = new Window("Hello window"); 
        setMainWindow(main);
        
        // Create the application data instance
        AppData sessionData = new AppData(this);
        
        // Register it as a listener in the application context
        getContext().addTransactionListener(sessionData);
        
        // Initialize the session-global data
        AppData.initLocale(getLocale(),
                           MyAppCaptions.class.getName());
        
        // Also set the user data model
        AppData.setUserData("Billy");
        
        // Now, we do not pass this application object
        // in the constructor, so it couldn't access the
        // app data otherwise.
        main.addComponent(new MyComponent());
    }
}