Skip to content

Commit

Permalink
Document wicket integration and wicket examples (WBX-26)
Browse files Browse the repository at this point in the history
  • Loading branch information
Clint Popetz committed Apr 18, 2009
1 parent 551f9cf commit c0603c2
Show file tree
Hide file tree
Showing 4 changed files with 275 additions and 5 deletions.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
139 changes: 135 additions & 4 deletions reference/en-US/ri.xml
Expand Up @@ -650,8 +650,8 @@ public class Game
The numberguess for Tomcat differs in a couple of ways. Firstly,
Web Beans should be deployed as a Web Application library in
<literal>WEB-INF/lib</literal>. For your convenience we provide a
single jar suitable for running Web Beans on Tomcat
<literal>webbeans-tomcat.jar</literal>.
single jar suitable for running Web Beans in any servlet container
<literal>webbeans-servlet.jar</literal>.
</para>

<tip>
Expand All @@ -663,16 +663,147 @@ public class Game
</tip>

<para>
Secondly, we need to explicitly specify the Tomcat servlet listener
Secondly, we need to explicitly specify the servlet listener
(used to boot Web Beans, and control it's interaction with requests)
in <literal>web.xml</literal>:
</para>

<programlisting><![CDATA[<listener>
<listener-class>org.jboss.webbeans.environment.tomcat.Listener</listener-class>
<listener-class>org.jboss.webbeans.environment.servlet.Listener</listener-class>
</listener>]]></programlisting>

</section>

<section id="numberguessWicket">
<title>The numberguess example in Apache Wicket</title>
<para>Although the JSR299 specification includes detailed information on how WebBeans integrates
with Java Server Faces, it is possible to use WebBeans injection, as well as the WebBeans conversational
context, with other web frameworks. In this section, we'll look at the module which allows integration of
WebBeans with the Apache Wicket framework. This documentation assumes that you have some familiarity
with Wicket; if not, please visit <ulink url="http://wicket.apache.org/">http://wicket.apache.org/</ulink>
to learn more. In this section we'll look at the previous "numberguess" example as implemented with Wicket.
We'll look at the conversational aspects of the Wicket/WebBeans integration in <xref linkend="wicketContexts"/></para>

<para>Like the previous example, the Wicket WebBeans examples make use of the <literal>webbeans-servlet</literal> module.
The use of the <ulink url="http://jetty.mortbay.org/">Jetty servlet container</ulink> is common in the Wicket community,
and is chosen here as the runtime container in order to facilitate comparison between the standard Wicket examples and these examples,
and also to show how the webbeans-servlet integration is not dependent upon tomcat as the servlet container. Since we
are deploying to a servlet container, we are also building as a WAR. In addition, these examples make use of the Eclipse IDE,
although instructions are also given to deploy the application from the command line.</para>

<section>
<title>Creating the eclipse project</title>
<para>To generate an eclipse project from the example, use apache maven and the eclipse:eclipse
plugin:
<programlisting><![CDATA[cd examples/wicket/numberguess
mvn eclipse:eclipse -DdownloadSources]]></programlisting>
Then, from eclipse, choose <literal>File -> Import -> General -> Existing Projects into Workspace</literal>, choose
the root directory of the numberguess example, and click finish. This should result in a project in
your workspace named <literal>webbeans-wicket-numberguess</literal>:
<mediaobject>
<imageobject>
<imagedata fileref="images/wicket-numberguess-project.png" format="png"/>
</imageobject>
</mediaobject>
</para>
</section>

<section>
<title>Running the example from eclipse</title>
<para>This project follows the <literal>wicket-quickstart</literal>
approach of creating an instance of <literal>jetty</literal> through a <literal>Start</literal>
class. So running the example is as simple as right-clicking on that Start class in src/test/java in the package
explorer and choosing "Run as Java Application." You should then see console output related to jetty starting up,
and then be able to hit <literal>http://localhost:8080</literal> to view the app. To stop the app, switch to the Console
View and click the red stop square. One can also debug in the same fashion, by choosing "Debug as Java Application" when right-clicking.
</para>
</section>
<section>
<title>Running the example from the command line in Jboss or Tomcat</title>
<para>This example can also be deployed from the command line in a fashion similar to the other examples. Assuming you have set up the
<literal>build.properties</literal> file in the <literal>examples</literal> directory to specify the location of Jboss or Tomcat, as previously
described, you can simply run <literal>ant deploy</literal> from the <literal>examples/wicket/numberguess</literal> directory, and access
the application at <literal>http://localhost:8080/webbeans-numberguess-wicket</literal>.
</para>
</section>

<section>
<title>Understanding the code</title>
<para>While JSF uses unified EL expressions to bind view layer components in jsp or xhtml to
WebBeans, Wicket keeps a stricter separation of concerns in place. The markup is plain html with
a one-to-one mapping between html elements and the view components, which are constructed in java code. All
view logic, including binding of components to models and controlling the response of view actions,
is handled in java code. Therefore, the integration of WebBeans with Wicket takes place through annotated
injections of WebBeans into your WebPage subclass (or into other custom wicket component subclasses.)</para>

<para>The code in the wicket numberguess example is very similar to the JSF-based numberguess example described previously. The business logic
components (Game, Generator, MaxNumber, etc.) are the same. Differences include the following:</para>
<itemizedlist>
<listitem><para>Each wicket application must have a <literal>WebApplication</literal> subclass,
and in the case of the Wicket/WebBeans integration, this class should subclass <literal>
org.jboss.webbeans.wicket.WebBeansApplication</literal>. (If you would prefer not to subclass
WebBeansApplication, you can manually add the small number of overrides and listeners to your own WebApplication
subclass. See the javadocs on WebBeansApplication for details.) In our case, our application class is called<literal>
SampleApplication</literal>:
<programlisting><![CDATA[public class SampleApplication extends WebBeansApplication
{
@Override
public Class getHomePage()
{
return HomePage.class;
}
}]]></programlisting>This class specifies which page wicket should treat as our HomePage,
in our case, <literal>HomePage.class</literal></para>
</listitem>
<listitem>
<para>In <literal>HomePage</literal> we see typical wicket code to set up page elements. The
bits that are interesting with respect to WebBeans are the injection of the <literal>Game</literal>
WebBean the top of the class</para>
<programlisting><![CDATA[ @Current Game game;]]></programlisting> which is then used later in,
for example, the code for submitting a guess:<programlisting><![CDATA[final Component guessButton = new AjaxButton("GuessButton") {
protected void onSubmit(AjaxRequestTarget target, Form form) {
if (game.check()) {]]></programlisting>This injection is a proxy which holds a transient reference to the actual
Game WebBean, and is therefore safe with respect to the serialization of the wicket page. Actual
storage of the bean is in the session, and is managed by WebBeans. Note that Wicket components, like the HomePage and its
subcomponents, are <emphasis>not</emphasis> WebBeans. Their construction is handled directly by wicket (for
the page itself) and by the example code (for the nested page elements), and not by the WebBeans
Manager. However, the <literal>
webbeans-wicket</literal> integration takes care of injecting dependencies into the page for us. This
injection occurs through an <literal>IComponentInstantiationListener</literal> which is
registered by the <literal>WebbeansWebApplication</literal>. This means that although wicket
components can have injections, they <emphasis>cannot</emphasis> use interceptors and the other features
of real WebBeans. The delegate beans used by the wicket code can of course be full WebBeans,
as the <literal>Game</literal> bean is in this example.</listitem>
<listitem><para>Note that this example also uses ajax for processing of button events, and
dynamically hides buttons that are no longer revelant, for example when the user has won the game.</para></listitem>

<listitem><para>In order to activate wicket for this webapp, the Wicket filter is added to web.xml, and our application class is specified:
<programlisting><![CDATA[ <filter>
<filter-name>wicket.numberguess-example</filter-name>
<filter-class>org.apache.wicket.protocol.http.WicketFilter</filter-class>
<init-param>
<param-name>applicationClassName</param-name>
<param-value>org.jboss.webbeans.examples.wicket.SampleApplication</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>wicket.numberguess-example</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.jboss.webbeans.environment.servlet.Listener</listener-class>
</listener>]]></programlisting>Note that the servlet listener is also added, as in the tomcat example, in order to boostrap
WebBeans when jetty starts, and to hook WebBeans into the jetty servlet request and session lifecycles.</para>
</listitem>

</itemizedlist>


</section>

</section>
</section>

<section>
Expand Down
141 changes: 140 additions & 1 deletion reference/en-US/scopescontexts.xml
Expand Up @@ -247,8 +247,147 @@ public class OrderBuilder {

</section>

<section id="wicketContexts">
<title>Conversations using Wicket</title>
<para>The conversation scope can also be used in WebBeans with the Apache Wicket web framework, through the
<literal>webbeans-wicket</literal> module. This module takes care of:
<itemizedlist>
<listitem><para>Setting up the conversation context at the beginning of a wicket request, and tearing it down afterwards</para></listitem>
<listitem><para>Storing the id of any long-running conversation in Wicket's WebPage metadata when the page response is complete</para></listitem>
<listitem><para>Activating the correct long-running conversation based upon which page is being accessed</para></listitem>
<listitem><para>Propagating the conversation context for any long-running conversation to new pages</para></listitem>
</itemizedlist> </para>
<section>
<title>Starting and stopping conversations in Wicket</title>
<para>As in WebBeans JSF applications, a conversation <emphasis>always</emphasis> exists for any request, but its lifetime is only that of the current request unless it is marked as
<emphasis>long-running</emphasis>. For Wicket applications this is accomplished exactly as in JSF applications, by injecting the <literal>@Current Conversation</literal> and then
invoking <literal>conversation.begin()</literal>. Likewise, conversations are ended with <literal>conversation.end()</literal>
</para>
</section>

<section>
<title>Long running conversation propagation in Wicket</title>
<para>
When a conversation is marked as long-running, the id of that conversation will be stored in Wicket's WebPage metadata for the current page.
In addition, if a new page is created and set as the response target through <literal>setResponsePage</literal>, this new page will also participate
in this conversation. This occurs for both directly instantiated pages, i.e. <literal>setResponsePage(new OtherPage())</literal> as well as for
bookmarkable pages created with <literal>setResponsePage(OtherPage.class)</literal> where <literal>OtherPage.class</literal> is mounted as bookmarkable
from your <literal>WebApplication</literal> subclass (or through annotations.) In the latter case, because the new page instance is not created until after
a redirect, the conversation id will be propagated through a request parameter, and then stored in page metadata after the redirect.
</para>
</section>
<section>
<title>The Wicket Conversations Example</title>
<para>In <literal>examples/wicket/conversations</literal> there is an example application for using conversations from Wicket. To setup and run the
application, follow the examples in <xref linkend="numberguessWicket"/>. If running within eclipse, the application is available at <literal>http://localhost:8080</literal>,
and if running within an external container, the app is available at <literal>http://localhost:8080/webbeans-conversations-wicket</literal>.
</para>
<section>
<title>Using the application</title>
<para>The <emphasis>conversations</emphasis> application is simply an interface to begin and end conversations, switch between them, and store a single piece of data in each.
The interface looks like the following:
<mediaobject>
<imageobject>
<imagedata fileref="images/wicket-conversations-snap.png" format="PNG"/>
</imageobject>
</mediaobject></para>
<para>
The menu at the top will show the list of long-running conversations, along with data about each,
as well as the active conversation (even if it is not long running.) The currently selected item
of this menu is the current conversation for the last request that was processed. Changing this menu
changes the current conversation. The "Data in conversation" text box and the submit
button labeled "Change Value" allow you to change a piece of string data associated
with the conversation. The "begin" button marks the conversation as long running.
The "noop" button submits the form without changing the state of the conversation.
The "end" button ends the current conversation. The "longop" button
executes a method that takes a few seconds, which can be used to play with conversation timeouts and
synchronization/locking. The "Abandon" link redirects to the HomePage without propagating
the conversation, thus starting a new temporary conversation. The conversation that has been abandoned
can be rejoined by selecting it in the popup.</para>
</section>
<section>
<title>Understanding the code</title>
<para>As in <xref linkend="numberguessWicket"/>, there is boilerplate code in the
SampleApplication, as well as in web.xml, to ensure correct integration of Wicket and Webbeans.
Other pieces of this application include:</para>
<itemizedlist>
<listitem><para>The <literal>Conversations</literal> class exists to allow configuration of the conversation
timeout for the current session<programlisting><![CDATA[@Produces
@ConversationInactivityTimeout
@Example
public static long getConversationTimeoutInMilliseconds()
{
return 600000;
}]]></programlisting>The <literal>Example</literal> deployment type is used to illustrate
custom deployment types. It is defined in Example.java:<programlisting><![CDATA[@Target( { TYPE, METHOD, FIELD })
@Retention(RUNTIME)
@Documented
@DeploymentType
public @interface Example
{
}]]></programlisting> and is activated in <literal>WEB-INF/beans.xml</literal>:
<programlisting><![CDATA[<Beans xmlns="urn:java:ee" xmlns:conversations="urn:java:org.jboss.webbeans.examples.conversations">
<Deploy>
<Standard />
<Production />
<conversations:Example />
</Deploy>
</Beans>]]></programlisting></para></listitem>
<listitem><para>The <literal>Data</literal> class is our conversational component that stores a
single string:<programlisting><![CDATA[@ConversationScoped
@Named
public class Data implements Serializable
{
private String data;
public String getData()
{
return data;
}
public void setData(String data)
{
this.data = data;
}
}]]></programlisting></para></listitem>
<listitem><para>The <literal>HomePage</literal> class is where
the brunt of the work takes place. It injects the WebBeans-provided <literal>ConversationManager</literal>
component in order to access the list of long-running conversations, injects the current <literal>Conversation</literal>
in order to allow control over whether that conversation is long-running, and injects the <literal>Data</literal>
component to allow its manipulation by the textfield:<programlisting><![CDATA[/**
* These are injections for displaying information about existing conversations to the user
*/
@Current ConversationManager conversationManager;
@Current Conversation currentConversation;
/**
* This is our conversational data component, to illustrate how data is used in conversations and
* exists per-conversation.
*/
@Current Data data;
]]></programlisting>The data component is referenced by the wicket TextField using a
<literal>PropertyModel</literal>:<programlisting><![CDATA[form.add(new TextField("dataField", new PropertyModel(this, "data.data")));]]></programlisting>
This means that for each page, the text field is referring to the "data" property of the "Data" bean that is specific to
the conversation that is active for the request, whether that be a transient conversation that is created for the request
or a long-running conversation associated with the page. If one types data into the text field
and clicks <literal>Change Value</literal>
when a long-running conversation is not started, and then clicks <literal>noop</literal>, the
value in the text field disappears, because it was associated with a transient conversation, and
upon redirect, the new conversation had a different Data object, which had the default (empty) value.</para>
<para>As you can see by the other code in this example, conversations are started and ended programmatically,
and no WebBeans-specific code is necessary to propagate the conversation. The only exception to this
is in the code to <emphasis>switch</emphasis> conversations, which is not a typical operation in real scenarios.
In this case, a conversation id (<literal>cid</literal>) request parameter is passed to the <literal>setResponsePage</literal>
invocation, and this explicit conversation id will override the id that the wicket/webbeans integration
code would normally generate in order to propagate the current conversation, and hence the specified
conversation will be active upon redirect.</para>
</listitem>
</itemizedlist>

</section>
</section>
</section>
</section>

<section>
<title>The dependent pseudo-scope</title>

Expand Down

0 comments on commit c0603c2

Please sign in to comment.