Discussions

News: A Preferences API Implementation for Web Applications

  1. The Java Preferences API provides a convenient and backend-neutral way of storing and retrieving simple data, especially user preferences, without requiring the application developer to explicitly manage its loading and saving. Though it is primarily used for desktop applications, these characteristics also make the Preferences API highly interesting to use in web applications. Through its ease and convenience of use, it allows for the creation of more complex applications. Most importantly, the concept of the Preferences allows for an intuitive storing of user preferences. However, while the Preferences API itself is very well suited to this task, its default implementations are not. This article aims to explain the need for a Preference API implementation specifically focused on the needs of web applications and to present a custom implementation addressing this need. Why create a new implementation? The Preferences API defines a hierarchical storage structure divided into two trees, a "system" tree and a "user" tree. However, it does not define any specific semantics for the terms "system" and "user." This is left to the individual implementations. Both default implementations – the WindowsPreferences used under Windows and the FileSystemPreferences used under Linux – interpret the "user" as the user currently logged into the operating system. This is appropriate for desktop application usage scenarios where at any given moment only a single user is logged in and interacting with the system. However, for web applications that concurrently serve multiple users, this interpretation is severely unsatisfactory as it does not discern between those users; changes to a user's preferences affect the preferences of all users. Instead, web applications require an interpretation of "user" that treats multiple users concurrently active on the same system as independent from each other, allowing to store the user-specific data for each of them separately. Architecture Such an implementation has been created for the wingS project. Though it is included in this project, it is completely independent of any wingS-specific functionality and can therefore without any problems be used separately. The implementation consists of three classes, namely ServletPreferences, ServletPreferencesFactory and ServletPreferencesFilter that reside in the org.wings.prefs package of the wingS Framework. The ServletPreferencesFactory is very simple: it implements the PreferencesFactory interface (java.util.prefs.PreferencesFactory) by calling the getUserRoot() and getSystemRoot() methods of the ServletPreferences class. The ServletPreferences class is the core of the custom preferences. It follows a filesystem-based approach closely resembling the FileSystemPreferences implementation, as this allows it to be used on any platform. The implementation of the system tree and the tree-independent methods is directly following the implementation of FileSystemPreferences except that the Linux-specific native file-locking has been left out in order to ensure the compatibility of the ServletPreferences with any operating system. The specificity of the ServletPreferences lies in the implementation of the user tree, or rather the user trees: for each logged in user, a separate node hierarchy exists, with its rootnode stored in a Hashmap and identified by the user name as a key. This obviously requires a means of recognizing a specific user beyond the lifetime of his sessions. In case of an authenticated user, this is achieved through retrieving the username from the user's java.security.Principal object which is associated to each of the user’s HttpServletRequests. In the case of non-authenticated users, this is however obviously not possible. In this case, ServletPreferences fall back to identifying the user via a unique ID stored in a Cookie. For each call to a node in the user hierarchy, i.e. via userRoot() or userNodeForPackage(), the user from whose request the call originates can then identified and the correct root node be located. The ServletPreferencesFilter is an implementation of the javax.servlet.Filter interface and serves to give the ServletPreferences access to the objects, which are needed in order to identify the user. Installation The source code can be obtained via SVN Checkout from:https://j-wings.svn.sourceforge.net/svnroot/j-wings/wings3/trunk/servlet-prefs and be built with Ant. As the Preferences base class regrettably does not allow a reloading of the preferences factory, the classes have to be added to the system classloader of the application server. Therefore, the generated .jar-file has to be copied to the application server’s lib directory, e.g. tomcat/lib or jboss/server/default/lib. Finally, the preferences filter has to be added to the deployment descriptor of each application using the ServletPreferences. This is done by adding the following lines to the application’s web.xml file: ServletPreferencesFilter org.wings.prefs.ServletPreferencesFilter ServletPreferencesFilter /* Usage Example One of the main benefits of the Preferences API is its convenience of use. Let us consider the following code sample: Preferences preferences = Preferences.userRoot(); preferences = preferences.node("org.wings.test"); preferences.put("foo", "bar"); preferences.flush();The first line serves to obtain a reference to the userRoot for the currently active user. Line 2 returns the current node's – in this case the userRoot's – child node named "org.wings.test". If this node does not exist yet, it is automatically created. Line 3 stores the String-value "bar" under the key "foo" into the node "org.wings.test". Note that the value is not yet guaranteed to be stored persistently. This is only ensured after the flush() method has been called on the node or one of its parent nodes, as in line 4. Retrieving the value is similarly simple: String value = preferences.get("foo", "default");returns the previously stored value. Each call of a get method requires, aside from the key, the passing of a default value that will be returned if no value associated to the specified key exists, or if the backing store is not available. In this example, the storing and retrieving of data is demonstrated for values of the type String. Similar methods exist for values of the types boolean, byte[], double, float, int and long. The storage of arbitrary serialized objects is not supported. Summary The default implementations of the Preferences API are not well suited for the use in web applications, as they do not support multiple concurrent users. A custom implementation that addresses this problem through the identification of individual users and the creation of a separate node hierarchy for each of them is presented in this article and the fundamentals of using the Preferences API are explained. Author: Christian Kochs
  2. Are the user preferences stored in a database / file by this API implementation? Or do I have to implement that feature?
  3. Storage[ Go to top ]

    The prefs API by default uses the native store for preferences of the OS it's running on. On Windows it uses the registry. On OSX it uses .plist in ~/Library/Preferences. For this port they've overridden that behavior and store the preferences in XML. No need to create a data store.
  4. Re: Storage[ Go to top ]

    The prefs API by default uses the native store for preferences of the OS it's running on. On Windows it uses the registry. On OSX it uses .plist in ~/Library/Preferences. For this port they've overridden that behavior and store the preferences in XML. No need to create a data store.
    It sounds like you're talking about the desktop application situation. What about the web application situation?
  5. Let me clarify my understanding of this implementation: it stores the user prefs data on the server side using XML? I have been using GWT and Echo2 to write desktop-like web GUI and storing user prefs using cookies. In my case, the prefs data for each user are not that large or complex, so cookies are OK. Has the WingS team (or anyone) considered implementing Prefs API and storing the data on the client side for web applications? cheers romen
  6. Re: Storing the preferences in cookies[ Go to top ]

    Hi Romen, storing the prefs in cookies was an option, that we've also diskussed. But then we decided, to not increase the network load with the transport of preferences data and better store the data on the server (where it is actually created and used). However, if the session has no user principal, we're using a cookie to identify the client across sessions. Then the cookie is the key to the individual preferences store. Regards, Holger
  7. In this case, ServletPreferences fall back to identifying the user via a unique ID stored in a Cookie.
    Have you taken any steps to secure this solution to avoid "any user" from accessing other users preferences?
  8. In this case, ServletPreferences fall back to identifying the user via a unique ID stored in a Cookie.


    Have you taken any steps to secure this solution to avoid "any user" from accessing other users preferences?
    Hmm..., John, isn't your question answered in the sentence before the one you cited?
    In the case of non-authenticated users, this is however obviously not possible. In this case, ServletPreferences fall back [...]
  9. In this case, ServletPreferences fall back to identifying the user via a unique ID stored in a Cookie.


    Have you taken any steps to secure this solution to avoid "any user" from accessing other users preferences?

    Hmm..., John, isn't your question answered in the sentence before the one you cited?
    In the case of non-authenticated users, this is however obviously not possible. In this case, ServletPreferences fall back [...]
    Only if you think that it is ok for anyone to access any non-authenticated users preferences. I am guessing that they have taken some steps to prevent this, like making an ID difficult to guess. I just want to know which.
  10. Hi, we did this implementation under the assumption that the stored information is not security- or privacy-relevant, so that such measures are currently not included. However, the generation of non-guessable IDs is clearly a useful suggestion which we are definitely planning to implement in the near future. Kind regards, Christian
  11. Administration[ Go to top ]

    This is interesting. Some of the use-cases for web based applications require an administrator to maintain a users profile (override settings, etc). How will this be accomplished? Is there a standard API to manage multiple user preferences?
  12. Re: Administration[ Go to top ]

    With this implementation you can do exactly, what the standard java preferences api is designed for. From within a session, it is totally transparent, that there might be other sessions from other users. Thus with the standard api, you will not be able to access another user's session. Though, you will have to derive from our implementation in order to accomplish that.
  13. The ServletPreferences does not check the entries in any way and allows preference nodes such as "../../../foo". Using a file system as a back-end might not be optimal anyhow, but in addition it creates security problems. Well, usually the nodes and keys are made internally by a web application, and not given by a user, so it shouldn't matter. I noticed the problem only because I made preferences manager test application that allows users to edit their preference entries and create/delete nodes, and didn't first notice to check the validity of the names. I tried the preferences module with IT Mill Toolkit. There's a screenshot at http://iki.fi/~magi/tmp/itmill/prefsmanager.png . I put the test application at http://iki.fi/~magi/tmp/itmill/prefsmanager.war (requires the servlet-prefs.jar be installed on the server) and the source code at http://iki.fi/~magi/tmp/itmill/itmill-prefsmgr-20071223.tar.gz . (Just a test - it doesn't check the validity of the node names.) While the idea of the preferences servlet implementation is good, I'd consider some other than filesystem storage. Also running it as a filter doesn't seem necessary and creates problems for setting the user name, if using authenticated sessions. I don't know how wingS manages sessions, but for IT Mill Toolkit using the HttpServletRequest object to get the user name would be kind of problematic as users are managed at different level. It would seem to be easiest to access the preferences as a regular application object and not through a filter. Well, probably. Or is it possible to use it not as a filter and provide the username somehow? Strange, for some reason I have to restart Tomcat every time I modify my app. Something gets out of sync with the filter.
  14. While the idea of the preferences servlet implementation is good, I'd consider some other than filesystem storage. Also running it as a filter doesn't seem necessary and creates problems for setting the user name, if using authenticated sessions. I don't know how wingS manages sessions, but for IT Mill Toolkit using the HttpServletRequest object to get the user name would be kind of problematic as users are managed at different level. It would seem to be easiest to access the preferences as a regular application object and not through a filter. Well, probably. Or is it possible to use it not as a filter and provide the username somehow?
    The file system store doesn't introduce additional dependencies. It works out of the box. This is why we chose the filesystem store. Using filters is the only way to make it framework independent.
    Strange, for some reason I have to restart Tomcat every time I modify my app. Something gets out of sync with the filter.
    As described in the article, you must put the servlet prefs classes into the system classloader. The preferences base class does not allow a reloading of the preferences factory. If the webapp is redeployed, then there are multiple versions of the servlet prefs classes and you're getting class cast exceptions.