blob: 4100c00b15f5216163b71c88282c0338ec8736c3 [file] [log] [blame]
In Wicket we use class _org.apache.wicket.Session_ to handle session-relative information such as client information, session attributes, session-level cache (seen in paragraph 8.2), etc...
In addition, we know from paragraph 8.1 that Wicket creates a user session to store versions of stateful pages. Similarly to what happens with RequestCycle, the new Session's instances are generated by the _Application_ class with the _newSession(Request request, Response response)_ method. This method is not declared as final, hence it can be overridden if we need to use a custom implementation of the Session class.
By default if our custom application class is a subclass of _WebApplication_, method _newSession_ will return an instance of class _org.apache.wicket.protocol.http.WebSession_. As we have mentioned talking about _RequestCycle_, also class Session provides a static _get()_ method which returns the session associated to the current thread.
=== Session and listeners
Similar to the _RequestCycle_, class _org.apache.wicket.Session_ also offers support for listener entities. With Session these entities must implement the callback interface _org.apache.wicket.ISessionListener_ which exposes only the _onCreated(Session session)_ method. As you might guess from its name, this method is called when a new session is created. Session listeners must be added to our application using a typed collection, just like we have done before with request cycle listeners:
[source,java]
----
@Override
public void init(){
super.init();
//listener initialization...
ISessionListener myListener;
//add a custom session listener
getSessionListeners().add(myListener)
}
----
=== Handling session attributes
The Session class handles session attributes in much the same way as the standard interface _javax.servlet.http.HttpSession_. The following methods are provided to create, read and remove session attributes:
* *setAttribute(String name, Serializable value):* creates an attribute identified by the given name. If the session already contains an attribute with the same name, the new value will replace the existing one. The value must be a serializable object.
* *getAttribute(String name):* returns the value of the attribute identified by the given name, or _null_ if the name does not correspond to any attribute.
* *removeAttribute(String name):* removes the attribute identified by the given name.
By default class WebSession will use the underlying HTTP session to store attributes. Wicket will automatically add a prefix to the name of the attributes. This prefix is returned by the WebApplication's method getSessionAttributePrefix().
=== Accessing the HTTP session
If for any reason we need to directly access to the underlying _HttpSession_ object, we can retrieve it from the current request with the following code:
[source,java]
----
HttpSession session = ((ServletWebRequest)RequestCycle.get()
.getRequest()).getContainerRequest().getSession();
----
Using the raw session object might be necessary if we have to set a session attribute with a particular name without the prefix added by Wicket. Let's say for example that we are working with Tomcat as web server. One of the administrative tools provided by Tomcat is a page listing all the active user sessions of a given web application:
image::./img/tomcat-admin-sessions.png[]
Tomcat allows us to set the values that will be displayed in columns “Guessed locale” and “Guessed User name”. One possible way to do this is to use session attributes named “Locale” and “userName” but we can't create them via Wicket's Session class because they would not have exactly the name required by Tomcat. Instead, we must use the raw _HttpSession_ and set our attributes on it:
[source,java]
----
HttpSession session = ((ServletWebRequest)RequestCycle.get().
getRequest()).getContainerRequest().getSession();
session.setAttribute("Locale", "ENGLISH");
session.setAttribute("userName", "Mr BadGuy");
----
=== Temporary and permanent sessions
Wicket doesn't need to store data into user session as long as the user visits only stateless pages. Nonetheless, even under these conditions, a temporary session object is created to process each request but it is discarded at the end of the current request. To know if the current session is temporary, we can use the isTemporary() method:
[source,java]
----
Session.get().isTemporary();
----
If a session is not temporary (i.e. it is permanent), it's identified by an unique id which can be read calling the getId() method. This value will be _null_ if the session is temporary.
Although Wicket is able to automatically recognize when it needs to replace a temporary session with a permanent one, sometimes we may need to manually control this process to make our initially temporary session permanent.
To illustrate this possible scenario let's consider project "BindSessionExample" where we have a stateless home page which sets a session attribute inside its constructor and then it redirects the user to another page which displays with a label the session attribute previously created. The code of the two pages is as follows:
Home page:
[source,java]
----
public class HomePage extends WebPage {
public HomePage(final PageParameters parameters) {
Session.get().setAttribute("username", "tommy");
Session.get().bind();
setResponsePage(DisplaySessionParameter.class);
}
}
----
Target page:
[source,java]
----
public class DisplaySessionParameter extends WebPage {
public DisplaySessionParameter() {
super();
add(new Label("username", (String) Session.get().getAttribute("username")));
}
}
----
Again, we kept page logic very simple to not over-bloat the example with unnecessary code. In the snippet above we have also bolded Session's bind() method which converts temporary session into a permanent one. If the home page has not invoked this method, the session with its attribute would have been discarded at the end of the request and the page _DisplaySessionParameter_ would have displayed an empty value in its label.
=== Discarding session data
Once a user has finished using our web application, she must be able to log out and clean any session data. To be sure that a permanent session will be discarded at the end of the current request, class Session provides the invalidate() method. If we want to immediately invalidate a given session without waiting for the current request to complete, we can invoke the invalidateNow() method.
WARNING: Remember that invalidateNow() will immediately remove any instance of components (and pages) from the session, meaning that once we have called this method we won't be able to work with them for the rest of the request process.
=== Storing arbitrary objects with metadata
JavaServer Pages Specification1 defines 4 scopes in which a page can create and access a variable. These scopes are:
* *request:* variables declared in this scope can be seen only by pages processing the same request. The lifespan of these variables is (at most) equal to the one of the related request. They are discarded when the full response has been generated or when the request is forwarded somewhere else.
* *page:* variables declared in this scope can be seen only by the page that has created them.
* *session:* variables in session scope can be created and accessed by every page used in the same session where they are defined.
* *application:* this is the widest scope. Variables declared in this scope can be used by any page of a given web application.
Although Wicket doesn't implement the JSP Specification (it is rather an alternative to it), it offers a feature called metadata which resembles scoped variables but is much more powerful. Metadata is quite similar to a Java Map in that it stores pairs of key-value objects where the key must be unique. In Wicket each of the following classes has its own metadata store: RequestCycle, Session, Application and Component.
The key used for metadata is an instance of class _org.apache.wicket.MetaDataKey<T>_. To put an arbitrary object into metadata we must use the setMetaData method which takes two parameters as input: the key used to store the value and the value itself. If we are using metadata with classes Session or Component, data object must be serializable because Wicket serializes both session and component instances. This constraint is not applied to metadata of classes Application and RequestCycle which can contain a generic object. In any case, the type of data object must be compatible with the type parameter T specified by the key.
To retrieve a previously inserted object we must use the _getMetaData(MetaDataKey<T> key)_ method. In the following example we set a _java.sql.Connection_ object in the application's metadata so it can be used by any page of the application:
Application class code:
[source,java]
----
public static MetaDataApp extends WebApplication{
//Do some stuff...
/**
* Metadata key definition
*/
public static MetaDataKey<Connection> connectionKey = new MetaDataKey<> (){};
/**
* Application's initialization
*/
@Override
public void init(){
super.init();
Connection connection;
//connection initialization...
setMetaData(connectionKey, connection);
//Do some other stuff..
}
}
----
Code to get the object from the metadata:
[source,java]
----
Connection connection = Application.get().getMetaData(MetaDataApp.connectionKey);
----
Since MetaDataKey<T> class is declared as abstract, we must implement it with a subclass or with an anonymous class (like we did in the example above).