Help

Seam 2.1.0.BETA1 brought Wicket support to Seam, but how do you use this support?

In this first tutorial we will discuss how to create a seam-wicket project, and how to inject Seam components into Wicket components. A follow-up tutorial will go into more depth on orchestrating a Wicket application with Seam.

First, let's set up a new project using seam-gen which will create a skeleton project for us. Although seam-gen is designed for use with JSF, we can easily customize the project it creates to work with Wicket.

$ ./seam setup
Buildfile: build.xml

init:

setup:
     [echo] Welcome to seam-gen :-)
    [input] Enter your Java project workspace (the directory that contains your Seam projects) [C:/Projects] [C:/Projects]
/workspace
    [input] Enter your JBoss home directory [C:/Program Files/jboss-4.2.3.GA] [C:/Program Files/jboss-4.2.3.GA]
/Applications/jboss-4.2.3.GA
    [input] Enter the project name [myproject] [myproject]
wicket-tutorial
     [echo] Accepted project name as: wicket_tutorial
    [input] Do you want to use ICEfaces instead of RichFaces [n] (y, [n])
n
    [input] skipping input as property icefaces.home.new has already been set.
    [input] Select a RichFaces skin [blueSky] ([blueSky], classic, ruby, wine, deepMarine, emeraldTown, japanCherry, DEFAULT)
ruby
    [input] Is this project deployed as an EAR (with EJB components) or a WAR (with no EJB support) [ear] ([ear], war)
war
    [input] Enter the Java package name for your session beans [com.mydomain.wicket_tutorial] [com.mydomain.wicket_tutorial]
<ENTER>
    [input] Enter the Java package name for your entity beans [com.mydomain.wicket_tutorial] [com.mydomain.wicket_tutorial]
<ENTER>
    [input] Enter the Java package name for your test cases [com.mydomain.wicket_tutorial.test] [com.mydomain.wicket_tutorial.test]
<ENTER>
    [input] What kind of database are you using? [hsql] ([hsql], mysql, oracle, postgres, mssql, db2, sybase, enterprisedb, h2)
<ENTER>
    [input] Enter the Hibernate dialect for your database [org.hibernate.dialect.HSQLDialect] [org.hibernate.dialect.HSQLDialect]
<ENTER>
    [input] Enter the filesystem path to the JDBC driver jar [../lib/hsqldb.jar] [../lib/hsqldb.jar]
<ENTER>
    [input] Enter JDBC driver class for your database [org.hsqldb.jdbcDriver] [org.hsqldb.jdbcDriver]
<ENTER>
    [input] Enter the JDBC URL for your database [jdbc:hsqldb:.] [jdbc:hsqldb:.]
<ENTER>
    [input] Enter database username [sa] [sa]
<ENTER>
    [input] Enter database password [] []
<ENTER>
    [input] Enter the database schema name (it is OK to leave this blank) [] []
<ENTER>
    [input] Enter the database catalog name (it is OK to leave this blank) [] []
<ENTER>
    [input] Are you working with tables that already exist in the database? [n] (y, [n])
n
    [input] Do you want to drop and recreate the database tables and data in import.sql each time you deploy? [n] (y, [n])
y
[propertyfile] Creating new property file: /workspace/seam/seam-gen/build.properties
     [echo] Installing JDBC driver jar to JBoss server
     [copy] Copying 1 file to /Applications/jboss-4.2.3.GA/server/default/lib
     [echo] Type 'seam create-project' to create the new project

BUILD SUCCESSFUL
Total time: 44 seconds

Next, we need to create the project:

$ ./seam create-project

Now, we need to alter the project to support wicket. Lets first remove the JSF specific configuration files: /resources/WEB-INF/pages.xml, /resources/WEB-INF/faces-config.xml, /view/*.xhtml and /view/*.page.xml. Next, we need to edit /resources/WEB-INF/web.xml to remove the JSF servlet, its mapping, and any JSF, Facelets and RichFaces configuration parameter. You should end up with a web.xml like this:

<?xml version="1.0" ?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
      http://java.sun.com/xml/ns/javaee
      http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" 
   version="2.5">
    
   <listener>
      <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
   </listener>
    
   <filter>
      <filter-name>Seam Filter</filter-name>
      <filter-class>org.jboss.seam.servlet.SeamFilter</filter-class>
   </filter>

   <filter-mapping>
      <filter-name>Seam Filter</filter-name>
      <url-pattern>/*</url-pattern>
   </filter-mapping>
        
   <servlet>
      <servlet-name>Seam Resource Servlet</servlet-name>
      <servlet-class>org.jboss.seam.servlet.SeamResourceServlet</servlet-class>
   </servlet>
    
   <servlet-mapping>
      <servlet-name>Seam Resource Servlet</servlet-name>
      <url-pattern>/seam/resource/*</url-pattern>
   </servlet-mapping>
   
</web-app>

And finally, we need to add the wicket libraries and seam-wicket integration library to the project. First, copy jboss-seam-wicket.jar from the /lib/ directory of the Seam distribution to your project's /lib/ directory. Then, make sure the wicket libraries get deployed into the archive by first removing jsf-facelets.jar and the various Seam-JSF integration libraries from the file /deployed-jars.list and adding line items for jboss-seam-wicket.jar and wicket.jar. The file should now contain the following list:

antlr-runtime.jar
core.jar
drools-compiler.jar
drools-core.jar
janino.jar
jboss-el.jar
jboss-seam.jar
jboss-seam-wicket.jar
jboss-seam-debug.jar
jbpm-jpdl.jar
mvel14.jar
slf4j-log4j12.jar
slf4j-api.jar
wicket.jar

Now would be a good time to update your IDE to reflect the libraries you are using. You'll probably need to edit the classpath of your IDE using the project buildpath screen.

Lets now set up the source directories in your project. We need to deploy our Wicket components with Seam super powers to a special directory /WEB-INF/wicket where Seam can grab hold of them and enhance them. Unfortunately, this gets a little hairy because you need to modify the Ant build script.

Start by creating a new source directory called /src/web which we'll use to store Wicket views. Then, we need to alter the build script to copy these compiled classes to /WEB-INF/wicket, you can add this target:

<target name="compileweb" 
         depends="init" 
         description="Compile the Wicket views from java source code">
   <mkdir dir="${war.dir}/WEB-INF/wicket"/>
   <!-- Compile the Wicket classes -->
   <javac classpathref="build.classpath" 
          destdir="${war.dir}/WEB-INF/wicket" 
          debug="${javac.debug}" 
          deprecation="${javac.deprecation}" 
          nowarn="on">
      <src path="src/web" />
   </javac>
   <!-- Copy the html markup to the same location -->
   <copy todir="${war.dir}/WEB-INF/wicket">
      <fileset dir="src/web">
         <include name="**/*.html"/>
      </fileset>
   </copy>
</target>

And make sure it is run, by adding compileweb as a dependency for the war target:

<target name="war" 
         depends="compilemodel,compileactions, compileweb, copyclasses" 
         description="Build the distribution .war file">
TIP You may want to create a target named compile which depends compilemodel, compileactions, compileweb, copyclasses and then have the war target depend on compile, if this change has not already been made in Seam.

Wicket uses a class to configure the application (similar in nature to /WEB-INF/faces-config.xml for JSF); so before we can deploy the application to the server, we need to create the following class in the com.mydomain.wicket_tutorial.web package under the src/web directory:

public class TutorialApplication extends SeamWebApplication {}

We tell Wicket to use this class by adding a component definition to the Seam component descriptor (i.e., /resources/WEB-INF/components.xml). Start by adding the wicket namespace:

<components xmlns="http://jboss.com/products/seam/components"
   ...
   xmlns:wicket="http://jboss.com/products/seam/wicket">

Then configure the Wicket application by adding the following component definition:

<wicket:web-application
   application-class="com.mydomain.wicket_tutorial.web.TutorialApplication"/>

Now we can get to the real development. Let's first define a home page for Wicket; we'll make a really simple one for now. First, we need some html to layout and style our page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
   <head>
   </head>
   <body>
      <div style="font-weight:bold">
         <p wicket:id="replace">Wicket will replace this text at runtime.</p>
      </div>
   </body>
</html>

Each page in Wicket is accompanies by a matching tree of components in Java. The following class pairs with the home page we just created:

public class HomePage extends WebPage {

    public HomePage(PageParameters parameters) {
        add(new Label("replace", "Hello World!"));
    }

}

Finally, we need to tell Wicket to use our home page as the application's start page:

public class TutorialApplication extends SeamWebApplication {

    @Override
    public Class getHomePage() {
        return HomePage.class;
    }
    ...
}

Admittedly, it's taken a bit of time to get a Wicket project set up, but now that we have, we can start adding Seam to the mix.

TIP For those of you who are adventurous, and perhaps comfortable with Ant and Freemarker, you are encouraged to hack the seam-gen templates to create your own Wicket starter application. That way, you never have to repeat these steps again.

Now, let's make the application do something interesting. We'll add a login box to the home page and use the Seam authenticator to authenticate the user. Here's what a typical login form might look like in Wicket:

<div style="border: 1px solid black; width: 400px; padding: 3px">
   <form wicket:id="login">
      <div>
         <label for="username">Username</label>
         <input wicket:id="username" />
      </div>
      <div>
         <label for="password">Password</label>
         <input wicket:id="password" type="password" />
      </div>
      <div>
         <input type="submit" value="Login"/>
      </div>
   </form>
</div>

Of course, we also need to build a matching component tree in Java:

public class HomePage extends WebPage {

   public HomePage(PageParameters parameters) {
      add(new Label("replace", "Hello World!"));
      add(new LoginForm("login"));
   }

   public class LoginForm extends Form {
      public LoginForm(String id) {
         super(id);
         add(new TextField("username"));
         add(new PasswordTextField("password"));
      }
   }

}

Notice how the Wicket tags are acting as slots where the Java code can inject real UI components. But how do we capture the information that the user submits using the Wicket-powered login form? This is an area where Wicket really shines. There is no need to touch the html page. We can just manipulate the Java components to get what we want. First, let's inject the identity Seam component and tell Wicket to call authenticate() when the form is submitted. At this point, we aren't looking at the values the user entered.

public class LoginForm extends Form {

    @In Identity identity;
    @Logger log log;
		
    public LoginForm(String id) {
        super(id);
        add(new TextField("username"));
        add(new PasswordTextField("password"));
    }
		
    protected void onSubmit() {
        try {
            identity.authenticate();
            log.info("Login succeeded");
            setResponsePage(HomePage.class);
        } catch (LoginException e) {
            log.info("Login failed");
            error("Login failed");                    
        }
    }
	
}

The onSubmit() callback method will be called by Wicket when the user submits the form. In it, we tell Wicket to redirect us back to the current page if login succeeds, or raise an error if the login fails. Notice how we use exactly the same annotations as you use in a regular Seam application to inject a Seam component, including the @Logger annotation. For more information about Seam's bijection mechanism, see the bijection section of the Seam reference documentation.

All that is left is to bind the input controls to the username and password properties of the built-in Seam component named credentials so that we can access the values the user submits in the login routine. To do this, we inject the credentials component and bind it using the Wicket PropertyModel:

public class LoginForm extends Form {

    @In Identity identity;
    @In Credentials credentials;
		
    public LoginForm(String id) {
        super(id);
        add(new TextField("username", new PropertyModel(credentials, "username")));
        add(new PasswordTextField("password", new PropertyModel(credentials, "password")));
    }
    ...
}

Now, try logging in with the username admin and take a look at the console - you'll see your login succeeded message printed. (The default authenticator class in a seam-gen 2.1 project requires the username to be admin and the password to be blank).

That concludes our introduction to using Seam with Wicket. In this tutorial we've looked at creating a new Seam project, removing the JSF view layer and replacing it with Wicket, and using injection to access our Seam components from Wicket. In the next tutorial we'll look at controlling conversations using annotations on Wicket components, raising events, and more bijection (including outjection).

14 comments:
 
30. Sep 2008, 23:49 CET | Link

Pete,

This is great! Looking forward to following along and the upcoming tutorials around conversations and events!

Chris

ReplyQuote
 
01. Oct 2008, 07:37 CET | Link

Very nice.

One question. Is SSL been supported by the entry in components.xml or should I use the Wicket solution described in last entry of http://cwiki.apache.org/WICKET/how-to-switch-to-ssl-mode.html ?

Looking forward to a article describing conversation boundary in Wicket.

Marcell

 
01. Oct 2008, 13:48 CET | Link
Is SSL been supported by the entry in components.xml or should I use the Wicket solution described in last entry of http://cwiki.apache.org/WICKET/how-to-switch-to-ssl-mode.html ?

Use the wicket solution :-)

 
01. Oct 2008, 23:49 CET | Link
Paolo

This looks really, really promising. But when/how are enhanched the Wicket component by Seam? At build time, with a post-compile ANT task or on-fly by Seam at runtime ?

Anyway great work! Thank you

 
02. Oct 2008, 07:48 CET | Link

This is awesome, finally an alternative for Seam. I will use it, I love Wicket and Seam. Now I can just dump JSF. This is how really Web development should be, EASY.

 
02. Oct 2008, 11:36 CET | Link
Paolo wrote on Oct 01, 2008 23:49:
But when/how are enhanched the Wicket component by Seam? At build time, with a post-compile ANT task or on-fly by Seam at runtime ?

When Seam starts up (it writes to the log all the classes it enhances). An ant task would be nice too - file an issue in the Seam JIRA and we can look at adding it (especially if you write a patch!).

18. Oct 2008, 15:26 CET | Link
elhakim

you said : Then, make sure the wicket libraries get deployed into the archive by first removing jsf-facelets.jar and the various Seam-JSF integration libraries from the file /deployed-jars.list and adding line items for jboss-seam-wicket.jar and wicket.jar. The file should now contain the following list.

project generated by seam 2.10 CR1 does not have that file, there are only deployed-jars-war.list and deployed-jars-ear.list, which one should be edited?....

19. Oct 2008, 01:29 CET | Link
elhakim wrote on Oct 18, 2008 15:26:
you said : Then, make sure the wicket libraries get deployed into the archive by first removing jsf-facelets.jar and the various Seam-JSF integration libraries from the file /deployed-jars.list and adding line items for jboss-seam-wicket.jar and wicket.jar. The file should now contain the following list. project generated by seam 2.10 CR1 does not have that file, there are only deployed-jars-war.list and deployed-jars-ear.list, which one should be edited?....

I created a war in the tutorial, not an ear. But if you create an ear, you should add the Wicket jars to the war.

 
23. Jan 2009, 09:49 CET | Link
Stefan

Hi Pete! Thx for the great setup description. I followede the above but with the exception that I created a EAR project. I am working with seam-2.1.1GA and when a try to deploy the application I get some error when the war file deploys.

Partial stacktrace of deployment ... normal deploy sequence...

09:41:15,193 INFO  [Contexts] starting up: org.jboss.seam.security.facesSecurityEvents
09:41:15,193 INFO  [Initialization] done initializing Seam
09:41:15,209 INFO  [SeamFilter] Initializing filter: org.jboss.seam.web.wicketFilter
09:41:15,240 ERROR [[/seam_wicket]] Exception starting filter Seam Filter
java.lang.RuntimeException: exception invoking: unrwap

Caused by: java.lang.reflect.InvocationTargetException
...lines ommited
        ... 143 more
Caused by: java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory
        at org.apache.wicket.protocol.http.WicketFilter.<clinit>(WicketFilter.java:75)
        at org.jboss.seam.wicket.web.WicketFilterInstantiator.unrwap(WicketFilterInstantiator.java:47)
        ... 149 more
Caused by: java.lang.ClassNotFoundException: org.slf4j.LoggerFactory
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1358)
        at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1204)
        at java.lang.ClassLoader.loadClassInternal(Unknown Source)
        ... 151 more
09:41:15,256 ERROR [StandardContext] Error filterStart
23. Feb 2009, 16:29 CET | Link

I test the seam-wicket. But how to change the wicket development to deployment in components.xml? Thanks!

 
11. Jun 2009, 02:55 CET | Link

Is it possible to use the @Name seam annotation to define a seam component (bean in the war) and then inject that bean using the @In into a wicket component? I have an ear with an ejb and war. I can inject seam beans from the ejb into wicket components in the war just fine. I cannot name a bean in the war though and inject it into another bean in the war. I created the seam.properties file in the WEB-INF/classes folder, but no luck. Any ideas?

 
11. Jun 2009, 03:25 CET | Link

I was able to take the classes I was trying to inject using the @In annotation out of the web project and put them in their own j2ee utility project. After doing that, seam picked up the classes and made them available in the war. This came about because I wanted to use seam's bijection to inject some helper classes in some wicket components. I assumed I could put them in the war project directly. I still don't know if that is possible. But now I have an ear with an ejb, war, and utility jar and it seams :) to work well. Thanks for your article. -David.

 
04. Aug 2009, 10:41 CET | Link

It seems now suddenly I get an error message when running my tests. It says:

  [testng] WARN  [org.jboss.seam.wicket.web.WicketFilterInstantiator] No wicket components directory specified to give Seam super powers to
   [testng] FAILED: getDefaultContacts
   [testng] java.lang.IllegalStateException: Must set application-class using <wicket:web-application /> in components.xml

But I am not running wicket. Even if I add this to components.xml it still shows the same error message. Why?

 
04. Aug 2009, 16:20 CET | Link

I found it out.

Solution here

Post Comment