Archive

Posts Tagged ‘Tutorial’

Hibernate Search Part 2 – Document Search

January 19th, 2009

This is a continuation of my previous post Hibernate Search Part 1 – Database Search

I mentioned another cool feature that can be implemented using Hibernate search is the abilty to search inside other file types such as Microsoft Word documents. Using Apache POI, However the internetz where light on information on how to do this. I did find one tutorial On JavaWorld.com

However despite this tutorial being well written I found it overly complicated for a “Introduction” type post. This could be due to the fact it contains a lot of Spring and I know next to nothing about Spring. I did get it working by stripping out the key parts and including them into my own simple example

I have a Documents class in which I store a filename, a file type a Blob containing the contains of a MS Word document.

package com.jsf.entities;

import com.jsf.util.WordDocHandlerBridge;
import java.io.Serializable;
import javax.persistence.*;
import org.hibernate.search.annotations.*;
import static org.jboss.seam.ScopeType.SESSION;
import org.jboss.seam.annotations.*;

@Entity
@Name("documentsdetails")
@Table(name = "DOCUMENTS")
@Scope(SESSION)
@SequenceGenerator(name = "DOCUMENT_SEQUENCE_GENERATOR", sequenceName = "DOCUMENTS_S")
@Indexed(index = "indexes/documents")
public class Documents implements Serializable {
public Documents() {
}

@Id
@DocumentId
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "DOCUMENT_SEQUENCE_GENERATOR")
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Field(index = Index.TOKENIZED)
public String getFileName() {
return fileName;
}

public void setFileName(String fileName) {
this.fileName = fileName;
}

public String getFileType() {
return fileType;
}

public void setFileType(String fileType) {
this.fileType = fileType;
}

@Field(name = "fileData", index = Index.TOKENIZED, store = Store.YES)
@FieldBridge(impl = WordDocHandlerBridge.class)
@Lob
@Basic(fetch = FetchType.EAGER)
public byte[] getFileData() {
return fileData;
}

public void setFileData(byte[] fileData) {
this.fileData = fileData;
}

private int id;
private String fileName;
private String fileType;
private byte[] fileData;
}

Documents.java My documents entity bean. Only real thing difference here compared to the entity bean from my previous post is the inclusion of @FieldBridge(impl = WordDocHandlerBridge.class) on getFileData, this tells Search how to handle the byte[] containing the contents of the Word Document. This is a copy of the one from JavaWorld.com

package com.jsf.util;

import java.io.*;
import org.apache.poi.hwpf.HWPFDocument;
import org.apache.poi.hwpf.usermodel.Range;
import org.apache.poi.poifs.filesystem.POIFSFileSystem;
import org.hibernate.search.bridge.StringBridge;

public class WordDocHandlerBridge implements StringBridge {

public String objectToString(Object arg0) {
ByteArrayInputStream bais = new ByteArrayInputStream((byte[]) arg0);
StringBuilder _result = new StringBuilder();
try {
POIFSFileSystem poiStream = new POIFSFileSystem(bais);
HWPFDocument doc = new org.apache.poi.hwpf.HWPFDocument(poiStream);
Range range = doc.getRange();
int np = range.numParagraphs();
for (int i = 0; i < np; i++) {
_result.append(range.getParagraph(i).text());
_result.append(" ");
}
} catch (IOException ex) {
ex.printStackTrace();
}
return _result.toString();
}
}

This takes the byte[] then converts it to a String which will be used for searching. This is a copy of the class given in the JavaWorld.com example

Finally I have a Session Bean which does the creates the Document elements, does the Indexing and does the search.

package com.jsf.controllers.documents;

import com.jsf.entities.Documents;
import java.io.*;
import java.util.List;
import javax.ejb.*;
import javax.persistence.*;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.hibernate.search.jpa.*;
import static org.jboss.seam.ScopeType.CONVERSATION;
import org.jboss.seam.annotations.*;

@Stateful
@Scope(CONVERSATION)
@Name("documentBean")
@Local(value = DocumentController.class)
public class DocumentBean implements DocumentController {
static Logger log = Logger.getLogger(DocumentBean.class.getName());
@PersistenceContext
private EntityManager em;

@In
private FullTextEntityManager entityManager;

public void uploadDocument() {
Documents doc = new Documents();
doc.setFileName("pew.doc");
doc.setFileType("Doc");
File aFile = new File("C:/example1.doc");
try {
FileInputStream inFile = null;
//Create Input Stream from File
inFile = new FileInputStream(aFile);
//Convert to byte Array
byte[] theBytes = new
byte[inFile.available()];
inFile.read(theBytes);
doc.setFileData(theBytes);
//Persit Doc
em.merge(doc);
//Index Documents
FullTextEntityManager fullTextEntityManager = Search.createFullTextEntityManager(entityManager);
List docs = entityManager.createQuery("select c from Documents as c").getResultList();
for (Documents doc1 : docs) {
fullTextEntityManager.index(doc1);
}
} catch (Exception e11) {
e11.printStackTrace();
}
doSearch();
}

public void doSearch() {

FullTextEntityManager fullTextEntityManager =
org.hibernate.search.jpa.Search.createFullTextEntityManager(entityManager);
//Do Search on FileData or FileName
String[] productFields = {"fileData", "FileName"};
MultiFieldQueryParser parser = new MultiFieldQueryParser(productFields, new StandardAnalyzer());
parser.setAllowLeadingWildcard(true);
org.apache.lucene.search.Query luceneQuery;
try {
//Search for the String "Pew" in all the indexed documents
luceneQuery = parser.parse("Pew");
List resultsList = fullTextEntityManager.createFullTextQuery(luceneQuery, Documents.class)
.getResultList();
// Return list of Documents which contain Pew in the Name or Data
log.debug("Results Found " + resultsList.size());
} catch (Exception e) {
e.printStackTrace();
}
}

@Remove
@Destroy
public void destroy() {
}
}

My file C:/example1.doc is a simple MS Word file which contains the phrase “Pew Pew Pew”

Running the uploadDocument method will create and Index a new Document object, running the search will then return a list of Document objects which contain the word “Pew” in the body.

@In
private FullTextEntityManager entityManager;

is a new component which needs to be added in order to index a set of documents. In order to get this minor changes need to be made to persistence.xml and components.xml

In practice Apache POI Word Document handling seems a bit fragile according to there site it only supports Word files made in versions of office up to and including 2003, Office 2007 is not supported. Also It doesn’t like Open Office word files it gives a strange error about properties not being set. I am not sure of the current status of the project itself as they say they are currently lacking a developer for the Word handling components

Attached is a zip file containing Documents.java WordDocHandlerBridge.java and DocumentBean.java also included are 2 sample word files and a copy of my persistence.xml and components.xml
Example Code

Pew

Pew

Hibernate, Seam, Tutorial , ,

Hibernate Search Part 1 – Database Search

January 19th, 2009

Hibernate Search is a project which allows you to execute full text query’s against a database using the Apache Lucene API

“Hibernate Search brings the power of full text search engines to the persistence domain model and Hibernate experience, through transparent configuration (Hibernate Annotations) and a common API.”

As part of my ongoing JBoss Seam research I was asked to investigate how we could run “proper” query’s against a number of fields in a database. With the additional possibility of ranking results in order of relavence. Hibernate search does this almost out of the box.

I was going to write another “Hello World” tutorial showing how to do this. Until I found an excellent tutorial by Mike Desjardins.

Mikes tutorial is as simple and straight forward as 1 I would have created with the exception that mine was going to be about books and not cheese.

OK his is more original but I made a logo I was going to use for mine. Well ok I used MS Paint on someone elses great image – (Sorry who ever you are I couldnt find a link to credit you)

Honest Jordans Discount Books Banner

Honest Jordans Discount Books Banner

Mike doesn’t implement Seam in his example but its obvious how it could be applied just stick the contents of the servlet in a Session bean and your done.

Like I said it is also possible to implement a ranking system based on the importance of one field over another this can be done by Implementing the @Boost annotation, this takes a floating point value as a parameter this Boost multiplying the ranking of a index by the specified amount. For example.. and I’m going borrow Mikes code here.


@Entity @Table(name="ORIGIN")
public class Origin {

@Id @Column(name="origin_id")
private Integer id;

@Field(index=Index.TOKENIZED)
@Boost 5f
@Basic@Basic @Column(name="name")
private String name;

@Version @Column(name="version")
private Integer version;

// Accessors omitted
}

@Entity @Table(name="MILK")
public class Milk {
@Id @Column(name="milk_id")
private Integer id;

@Field(index=Index.TOKENIZED)
@Boost 2f
@Basic @Column(name="name")
private String name;

@Version @Column(name="version")
private Integer version;

// Accessors omitted
}

As you can see I have applied the @Boost annotation to these entities the name of the place of Origin is given a boost value of 5 and the name of the milk has been given a boost value of 2.

Now assume the user entered “Scottish” into the search field and this returned 2 values a Origin with the name “Scottish” and a Milk with the name “Scottish” as the Origin has the boost of 5 it will be returned first allowing the results to be displayed by ranking.
This is how it is possible to search for basic database objects using Hibernate search.

In my next post I am going to talk about how it is possible to search a Microsoft Word document, Using Hibernate Search and Apache POI.

A Note with setting up Hibernate Search I had some problems getting the class path set up the deployment I downloaded seemed to be missing some of the required lib files I however found I did have all the correct files in the lib directory of my Seam 2.1.0 deployment,

Hibernate, Seam, Tutorial , ,

Introduction to testing with Selenium

December 15th, 2008

Selenium is a framework which allows for testing of a front end web application. This can be down via the Selenium IDE which allows you to “record” tests in firefox, using this way you carry out any function you wish on your site and Selenium monitors the steps carried out and is translates them into a series of commands which can then be run later to replay and carry out the test.

This makes it very easy for non programmers to create tests as it requires no code to be written. However any change made to the site requires the whole test be rerecorded, and the IDE only works in Firefox. In order to get something that works in IE the output of the IDE recorded test can be outputted into of the following programming languages Java, C#, Perl, Python and Ruby. Once in tests are have be outputted to a programming language, they can be modified and run against any browser using another Selenium tool the Selenium Remote Control

Personally I prefer using the remote control as it is easier to tweak a test in Java than it is to record a test in the IDE after I make a change to the layout of a site.

Using the Hello World JSF tutorial I created in this post JBoss Seam, JSF,Oracle and EJB3 Hello World I will show you how to quick it make a few changes to this application to implement to simple acceptance testing.

1 download junit.jar

2 download Selenium RC 1.0 beta 1

3 extract it somewhere, mine has been extracted to C:\selenium-remote-control-1.0-beta-1-dist\selenium-remote-control-1.0-beta-1

4 copy C:\selenium-remote-control-1.0-beta-1-dist\selenium-remote-control-1.0-beta-1\selenium-java-client-driver-1.0-beta-1\selenium-java-client-driver.jar to the lib directory of your JBoss server C:\jboss-4.2.3.GA\server\default\lib

5 create a new batch file selenium.bat to control the Selenium test server. This is the application which carries out the tests.

c:
cd C:\selenium-remote-control-1.0-beta-1-dist\selenium-remote-control-1.0-beta-1\selenium-server-1.0-beta-1
java -jar selenium-server.jar -Dhttp.proxyHost=localhost -Dhttp.proxyPort=4447 -port 4447

Ok so now selenium is installed its time to update our application. Add the selenium-java-client-driver.jar to your project classpath. Also add Junit.jar to your classpath this will let us use the Junit annotations.

2 changes are required to be made to the build.xml to include the selenium-java-client-driver.jar and a change is required to register.xhtml in order to make testing the form elements easy.

build.xml


< ?xml version="1.0"?>
<project default="deployDevelopmentEar">
<target name="createBeansJar" depends="clean">
<mkdir dir="${dest.dir}/beans"/>
<javac compiler="modern" source="1.5" destdir="${dest.dir}/beans">
<classpath location="${lib.dir}/jsf-api.jar"/>
<classpath location="${lib.dir}/jboss-seam.jar"/>
<classpath location="${lib.dir}/javaee.jar"/>
<classpath location="${lib.dir}/hibernate3.jar"/>
<classpath location="${lib.dir}/hibernate-annotations.jar"/>
<classpath location="${lib.dir}/junit-4.1.jar"/>
<classpath location="${lib.dir}/selenium-java-client-driver.jar"/>
<classpath location="${lib.dir}/ojdbc14.jar"/>
<src path="${beans_module.src}"/>
</javac>
<copy file="${beans_module.src}/seam.properties" todir="${dest.dir}/beans"/>

<jar destfile="${dest.dir}/jsf.jar" basedir="${dest.dir}/beans">
<metainf dir="${beans_module.src}/META-INF">
<include name="**/*.*"/>
</metainf>
</jar>
</target>
</project>

register.xhtml

< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">

<head>
<title>Register New User</title>
</head>
<f :view>
<h :form>
<s :validateAll>
<h :panelGrid columns="2">

<div id="Name">Name:</div><h :inputText value="#{userdetails.name}" required="true" id="NameInput"/>
<div id="Address">Address:</div><h :inputText value="#{userdetails.address}" required="true" id="AddressInput" />
<div id="City">City:</div><h :inputText value="#{userdetails.city}" required="true" id="CityInput"/>
<div id="Email">Email:</div> <h :inputText value="#{userdetails.email}" required="true" id="EmailInput" />
<div id="PhoneNumber">Phone Number:</div><h :inputText value="#{userdetails.phoneNumber}" required="true" id="PhoneNumberInput" />
<div id="Age">Age:</div><h :inputText value="#{userdetails.age}" required="true" id="AgeInput" />
</h>
</s>

<h :messages/>
<h :commandButton value="Register" action="#{userBean.count}" id="RegisterButton"/>
</h>
</f>
</html> 

updated versions of both files can be downloaded here: <UPLOAD FILE LOCATION TODO>

Now to implement our new test files:

JSFExample/beans/Test

create a new package Test under JSFExample/beans

Browser.java

This is our testing base class it configures our URL we want Selenium to test, the server port assigned to the variable

private static final int SERVER_PORT = 4447; is the port used to start the selenium and is the same one as specified in the selenium start up script.


package Test;

import com.thoughtworks.selenium.DefaultSelenium;

public class Browser {

  public static DefaultSelenium getInstance(String browser) {
    if (sel == null) {
      newBrowser(browser);
    } else if (browserNameChanged(browser)) {
      stop();
      newBrowser(browser);
    }
    return sel;
  }

  public static void stop() {
    sel.stop();
    sel = null;
  }

  public static void openPage(String page) {
    if (sel.isAlertPresent()) {
      System.err.println("Unexpected alert with message \"" + sel.getAlert() + "\"");
    }
    sel.open(LOCAL_ADDRESS + page);
    try {
      Thread.sleep(100l);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  public static boolean isElementPresent(String... elements) {
    for (String element : elements) {
      if (!sel.isElementPresent(element)) return false;
    }
    return true;
  }

  public static boolean isVisible(String... elements) {
    for (String element : elements) {
      if (!sel.isVisible(element)) return false;
    }
    return true;
  }

  private static void newBrowser(String browser) {
    sel = new DefaultSelenium(SERVER_HOST, SERVER_PORT, browser, LOCAL_ADDRESS);
    sel.start();
    sel.setTimeout("25000");
    lastBrowser = browser;
  }

  private static boolean browserNameChanged(String browser) {
    return !browser.equals(lastBrowser);
  }

  private static DefaultSelenium sel;
  private static final int SERVER_PORT = 4447;
  private static final String SERVER_HOST = "localhost";
  private static final String LOCAL_ADDRESS = "<a href="http://localhost:8080/web/">http://localhost:8080/web/</a>";
  private static String lastBrowser = "";
}

 

BrowserTest.java

This class specifies which web browsers I want the tests to be run on. In my post Running Multiple versions of Internet Explorer on your PC I set up my PC to run IE 5.5 IE6 and IE 7 on the same PC this class allows me to test on all these different versions by specifying them.

*iexplorer runs the PCs default version of Internet Explorer in this case IE 7 and

*firefox runs the default version of Firefox.

1. Any additional browsers can be used by specifying the full path however these may have issues with proxies, more about proxies later, and may not be supported by Selenium IE 5.5 is not supported. However some features still work. If you just want to test your default IE and Firefox comment out these to lines

//list.add(new String[]{"*custom C:\\Program Files\\MultipleIEs\\IE6\\iexplore.exe"});
// list.add(new String[]{"*custom C:\\Program Files\\MultipleIEs\\IE55\\iexplore.exe"});


package Test;

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.AfterClass;
import java.util.ArrayList;
import com.thoughtworks.selenium.DefaultSelenium;

@RunWith(Parameterized.class)
public class BrowserTest {

  @Parameterized.Parameters
  public static ArrayList data() {
    ArrayList<string []> list = new ArrayList</string><string []>();
   list.add(new String[]{"*iexplore"});
   list.add(new String[]{"*firefox"});
   list.add(new String[]{"*custom C:\\Program Files\\MultipleIEs\\IE6\\iexplore.exe"});
   list.add(new String[]{"*custom C:\\Program Files\\MultipleIEs\\IE55\\iexplore.exe"});
    return list;
  }

  @AfterClass
  public static void closeBrowser() {
    Browser.stop();
  }

  protected DefaultSelenium sel;
}

NavigationTest.java

This is our class where we specify our tests. As it extends BrowserTest.java it will execute on this class on each of the browsers in the BrowserTest list.

This class is very simple It contains a setup method, 2 tests and a clean up section.

The setup method is specified with @before and this opens the browser at the correct page.

Test 1 is checkFieldsArePresent this uses the ID on the input fields and simply makes sure they are present on the web page

Test 2 FillForm() enters detail into each of the fields on the form using the ID of the textInputFields to put the correct text in the correct fields. It then presses the register button, waits 5 seconds for the next page to be displayed and checks to see if the new person has been added,

As I am using an Oracle database I need to remove the details of the person I just added to prevent my database filling with duplicate data which could corrupt future tests. This is simply a JDBC connection to the database which executes a DELETE script on the person I just created.


package Test;

import org.junit.*;
import static org.junit.Assert.assertEquals;
import java.sql.*;
import com.thoughtworks.selenium.*;

public class NavigationTest extends BrowserTest {
  public String browser = "";

  public NavigationTest(String abrowser) {
    browser = abrowser;
    sel = Browser.getInstance(browser);
  }

  @Before
  public void seedDatabase() throws SQLException {
    Browser.openPage(TREE);
  }

  @Test
  public void checkFieldsArePresent() {
    waitFor(1000L);
    assertEquals(sel.getText("id=Name"), "Name:");
    assertEquals(sel.getText("id=Address"), "Address:");
    assertEquals(sel.getText("id=City"), "City:");
    assertEquals(sel.getText("id=Email"), "Email:");
    assertEquals(sel.getText("id=PhoneNumber"), "Phone Number:");
    assertEquals(sel.getText("id=Age"), "Age:");
  }

  @Test
  public void FillForm() {
    String[] browserArray = browser.split(" ");
    sel.type("j_id2:NameInput", "Test Guy " + browserArray[0]);
    sel.type("j_id2:AddressInput", "Test House");
    sel.type("j_id2:CityInput", "Test Town");
    sel.type("j_id2:EmailInput", Guy @ Test. com);
    sel.type("j_id2:PhoneNumberInput", "55554323434");
    sel.type("j_id2:AgeInput", "9");
    sel.click("j_id2:RegisterButton");
    waitFor(5000L);
    Assert.assertTrue(sel.isTextPresent("Test Guy " + browserArray[0]));
  }

  private void waitFor(Long millisecs) {
    try {
      Thread.sleep(millisecs);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

  @After
  public void cleanUpDB() {
    try {
      String[] browserArray = browser.split(" ");
      DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
      Connection con = DriverManager.getConnection
        ("jdbc:oracle:thin:@DBURL", "USERNAME", "PASSWORD");
      Statement stmt = con.createStatement();
      stmt.executeQuery("DELETE from Users where name='Test Guy " + browserArray[0] + "'");
      con.commit();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  DefaultSelenium sel;
  private final String TREE = "register.seam";
}

Acceptance.java

This class would form the core of a test suite, I would have all the test implementation classes for my site listed here, and running Acceptance would run all the tests for my suite.


package Test;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

@RunWith(Suite.class)
@Suite.SuiteClasses({
  NavigationTest.class

  })
public class Acceptance {
}

Testing

If you implement these 4 classes and specify your own database information in NavigationTest

Start the JBoss and run the selenium.bat

Now running Acceptance.java will cause a browser window to pop up which looks like

 

That concludes this tutorial on getting the Selenium test framework working with a simple JSF example.

 My personal view of Selenium is good, i have been using it for a while and it is relativity  simple to understand, and get working for most web interactions like clicking , typing etc. I haven’t been able to make it work yet with drag and drop using code written in the remote control. If anyone has please post a comment with a link on how you managed it. It can also be a bit flaky when it comes to timings. You often have to implement a wait for a few seconds otherwise it goes to fast and failed a test before a component has had time to load, which is what happens in this example if you don’t put a wait after the registerButton is clicked. 

Links and Tips

API

Selenium API

Forums

the forums content is OK however they are horrible to try and navigate, also expect lots of posts with no answers, I find most of these are for questions I am asking.

http://clearspace.openqa.org/community/selenium
Selenium Forum

Timing

If a test fails and it looks like it should pass It will probably be down to timing. Implement a few seconds wait before the failure point and it will probably work.

ID or xpath

You can specify a element either with the xpath or the element id, obviously ID is better and removes the need to mess about with firebug to try and find the xpath. You also don’t need to change every test if you make a simple UI change.

Proxy

In my example I use IE 6 and IE 7 however as I specify IE 6 as a custom browser you will have problems if the proxy to the selenium server is not setup. You wont have this problem with one of the default browsers.

To set the proxy for IE 6. open IE 7.. yes IE 7, go Tools > Internet Options > Lan settings and in the Proxy server box type localhost and in the port type 4447 and unclick bypass proxy server for local addresses.

This will stop your normal IE from working if you are behind a proxy but will make you tests work in IE 6, so remember to note down the original info before you change it.

JSF, Testing, Tutorial, selenium , , , , ,

JBoss Seam, JSF,Oracle and EJB3 Hello World

December 8th, 2008

In this first post I shall show you how to setup a simple “Hello World” type application using JSF , JBoss Seam and EJB3, I shall also be persisting data to an Oracle 9i database.

This post is not a introduction to Seam post there are many sources out there which provide a far clearer introduction to Seam than I can provide, This article is simply meant as a guide to getting Seam up and running. I initially had problems getting the basic demos up and running and found myself attempting to tie together multiple forum posts, blogs and tutorials to get a complete end to end setup working.

If you do require a Introduction to Seam I suggest the JBoss Seam manual

Or Micheal Yuan and Thomas Heutes book JBoss Seam: Simplicity and Power Beyond Java EE

Environment Setup:

I am using IntelliJ 8 as my IDE
Java JDK 1.5.0_09
I am running JBoss version 4.2.3 GA JBoss download page
with Seam version 2.1.0 SP1 Seam download page
I am also running a Oracle 9i server and using TOAD as a schema browser.

1. Extract JBoss, my JBoss is located at C:\JBoss-4.2.3.GA
2. Create a new project, In your IDE of choice create a new project called JSFExample. C:\JSFExample

3. Create the following directory structure: ignore the various properties files just now we will come back to them.

4. Setup your classpath the following jar files are required to get this example to work, almost all of these are found in the Seam 2.1.0 SP1 lib directory, with the exception of javaee.jar which is part of IntelliJ and ojdbc14.jar which is required in order to connect to an Oracle database. This can be downloaded from the Oracle site at Oracle download page

5. For simplicity’s sake copy these files with the exception of javaee.jar to the lib directory of your JBoss C:\JBoss-4.2.3.GA\server\default\lib and to the supportingJars directory for our project C:\JSFExample\supportingJars.
6. Start the JBoss server and make sure it starts successfully.

Database setup

In this example we will be entering information for some users, persisting this data to the database and returning a list of all users on successful submit. So create the following table in your database:

CREATE TABLE USERS
(
ID INTEGER NOT NULL,
NAME VARCHAR2(256 BYTE) NOT NULL,
PHONENUMBER VARCHAR2(16 BYTE),
ADDRESS VARCHAR2(256 BYTE),
CITY VARCHAR2(256 BYTE),
EMAIL VARCHAR2(256 BYTE),
AGE VARCHAR2 (3 BYTE)
)

OK so its not the best table in the world it but it serves our purpose for now, we also need a sequence to use with our primary key ID,

CREATE SEQUENCE USERS_S
START WITH 1

Coding – Server,

 

[UPDATE]Due to problems with the plugin thing I use to show code XML files tend to get a bit messed up and well, broken. I have uploaded the source to  http://jdick.co.uk/blog/code/JSFExample.zip None of the supporting jars are in this zip for bandwidth reasons you still have to go and get them yourself[/UPDATE] 

 

Users.java

So now we are almost finished setup lets get some code down. Start by creating a entity bean called Users in beans.com.jsf


package com.jsf;

import static org.jboss.seam.ScopeType.SESSION;
import java.io.Serializable;
import javax.persistence.*;
import org.hibernate.validator.Length;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;

@Entity
@Name("userdetails")
@Table(name = "USERS")
@Scope(SESSION)
@SequenceGenerator(name = "USER_SEQUENCE_GENERATOR", sequenceName = "USERS_S")
public class Users implements Serializable {
public Users() {
}

@Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "USER_SEQUENCE_GENERATOR")
public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

@Column(nullable = false)
@Length(min = 5, max = 25)
public String getAddress() {
return address;
}

public void setAddress(String address) {
this.address = address;
}

@Column(nullable = false)
@Length(min = 1, max = 3)
public String getAge() {
return age;
}

public void setAge(String age) {
this.age = age;
}

@Column(nullable = false)
@Length(min = 5, max = 15)
public String getCity() {
return city;
}

public void setCity(String city) {
this.city = city;
}

@Column(nullable = false)
@Length(min = 5, max = 30)
public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

@Column(nullable = false)
@Length(min = 5, max = 30)
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Column(nullable = false)
@Length(min = 5, max = 13)
public String getPhoneNumber() {
return phoneNumber;
}

public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}

private int id;
private String name;
private String phoneNumber;
private String address;
private String city;
private String email;
private String age;
}

note the name of the bean is “userdetails” this will be important later.

UserControllerBean.java

Next we need a session bean to carry out our operations. This could be a normal backing bean but I use EJB3 so a session bean it is, in this case a stateless session bean.


package com.jsf;

import javax.ejb.*;
import javax.persistence.*;
import java.util.List;
import org.jboss.seam.annotations.*;
import org.jboss.seam.annotations.datamodel.DataModel;

@Stateless
@Name("userBean")
@Local(value = Controller.class)
public class UserControllerBean implements Controller {

@In
private Users userdetails;

@PersistenceContext
private EntityManager em;

@DataModel
private List items = null;

public void count(String count) {
}

public String count() {
em.merge(userdetails);
return "/registered.seam";
}

@Factory("items")
public void getItems() {
List list = em.createQuery(FIND_ALL).getResultList();
items = list;
}

public static final String FIND_ALL = "SELECT userreturn FROM Users userreturn";
}

Controller.java

The last class we need is the Controller Interface so we can call the session bean from our web client.


package com.jsf;

import javax.ejb.Local;

@Local
public interface Controller {
public String count();
public void count(String count);
public void getItems();
}

Coding – Front End

In the web/resources directory create the following files

index.html

<html>
<head>
<meta http-equiv="Refresh" content="0; URL=register.seam">
</meta></head>
</html>

register.xhtml

< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">

<head>
<title>Register New User</title>
</head>
<f :view>
<h :form>
<s :validateAll>
<h :panelGrid columns="2">
Name: <h :inputText value="#{userdetails.name}" required="true"/>
Address: <h :inputText value="#{userdetails.address}" required="true"/>
City: <h :inputText value="#{userdetails.city}" required="true"/>
Email: <h :inputText value="#{userdetails.email}" required="true"/>
Phone Number: <h :inputText value="#{userdetails.phoneNumber}" required="true"/>
Age <h :inputText value="#{userdetails.age}" required="true"/>
</h>
</s>
<h :messages/>
<h :commandButton value="Register" action="#{userBean.count}"/>
</h>
</f>
</html>

Note that the value of the inputs is referred to as #{userdetails.name} the “userdetails” is the name of our entity bean and it is important that these match, when I was attempting to learn Seam I found quite a few examples which used a generic “user” variable everywhere with out pointing out the importance of this naming. Yes it may Seam obvious now but I’ve never been very good at picking up the obvious.

registered.xhtml


< ?xml version="1.0" encoding="utf-8"?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:s="http://jboss.com/products/seam/taglib"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">

<head>
<title>Successfully Registered New User</title>
</head>
<body>
<h :dataTable var="userreturn" value="#{items}" rendered="#{items.rowCount>0}" id="ResultsTable" >
</h><h :column>
<f :facet name="header">
<h :o utputText value="Name"/>
</f>
<h :o utputText value="#{userreturn.name}"/>
</h>
<h :column>
<f :facet name="header">
<h :o utputText value="Address"/>
</f>
<h :o utputText value="#{userreturn.address}"/>
</h>
<h :column>
<f :facet name="header">
<h :o utputText value="Phone Number"/>
</f>
<h :o utputText value="#{userreturn.phoneNumber}"/>
</h>
<h :column>
<f :facet name="header">
<h :o utputText value="City"/>
</f>
<h :o utputText value="#{userreturn.city}"/>
</h>
<h :column>
<f :facet name="header">
<h :o utputText value="Email"/>
</f>
<h :o utputText value="#{userreturn.email}"/>
</h>
<h :column>
<f :facet name="header">
<h :o utputText value="Age"/>
</f>
<h :o utputText value="#{userreturn.age}"/>
</h>

</body>
</html>

Another thing to note is if you are calling a method on the entity bean you miss out the prefix get/set so you use, “userdetails.name” and not “userdetails.setName” however if you are calling a method on the session bean you use the full method name as it is in the interface.

Config

OK so thats the code written but we still need to put all the various config files in place to get everything talking.

beans/META-INF

create the following files:

ejb-jar.xml

<ejb -jar 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/ejb-jar_3_0.xsd"
version="3.0">

<interceptors>
<interceptor>
</interceptor><interceptor -class>org.jboss.seam.ejb.SeamInterceptor</interceptor>

</interceptors>

<assembly -descriptor>
<interceptor -binding>
<ejb -name>*</ejb>
</interceptor><interceptor -class>org.jboss.seam.ejb.SeamInterceptor</interceptor>

</assembly>

</ejb>

persistance.xml

<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
</persistence><persistence -unit name="jsf">
<provider>
org.hibernate.ejb.HibernatePersistence
</provider>
<jta -data-source>
java:/OracleDS
</jta>
<properties>
<property name="hibernate.dialect"
value="org.hibernate.dialect.Oracle9Dialect"/>
<property name="hibernate.hbm2ddl.auto"
value="none"/>
</properties>
</persistence>

This persistance.xml is one specifically for a use with a standalone Oracle it will not overwrite any existing tables nor will it create any tables which do not all ready exist

In the root of beans create a file called Seam.properties this file is empty and is only needed in order to let the application know Seam is being used.

JSFExample/META-INF

application.xml

< ?xml version="1.0" encoding="UTF-8"?>
<application 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/application_5.xsd"
version="5">
<display -name>MyApp</display>
<module id="EJB">
<ejb>jsf.jar</ejb>
</module>
<module id="Web">
<web>
</web><web -uri>jsf.war</web>
<context -root>web</context>

</module>
</application>

and ejb-jar.xml the is the same code as shown above

web/WEB_INF

components.xml

< ?xml version="1.0" encoding="UTF-8"?>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:core="http://jboss.com/products/seam/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=
"http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.1.xsd
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.1.xsd">

<core :init jndi-pattern="jsf/#{ejbName}/local"/>

</components>

faces-config.xml

< ?xml version="1.0" encoding="UTF-8"?>
<faces -config version="1.2"
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-facesconfig_1_2.xsd">

<!-- Facelets support -->
<application>
<view -handler>com.sun.facelets.FaceletViewHandler</view>
</application>

</faces>

web.xml

< ?xml version="1.0" encoding="UTF-8"?>

<web -app version="2.5"
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">

<!-- Seam -->

<listener>
</listener><listener -class>org.jboss.seam.servlet.SeamListener</listener>

<context -param>
<param -name>javax.faces.DEFAULT_SUFFIX</param>
<param -value>.xhtml</param>
</context>

<servlet>
</servlet><servlet -name>Faces Servlet</servlet>
<servlet -class>javax.faces.webapp.FacesServlet</servlet>
<load -on-startup>1</load>

<servlet -mapping>
</servlet><servlet -name>Faces Servlet</servlet>
<url -pattern>*.seam</url>

<session -config>
</session><session -timeout>10</session>

</web>

Build script

The following is the build.xml and build.properties I have used for this example and It should work for you providing you have ANT installed and you have followed the same directory structure I have, with my project in C:\JSFExample and my JBoss in C:\JBoss-4.2.3.GA

build.xml

< ?xml version="1.0"?>
<project default="deployDevelopmentEar">

<property environment="env"/>
<property file="build.properties"/>

<path id="compile.classpath">
<pathelement location="${servlet.api.jar}"/>
<pathelement location="${jsp.api.jar}"/>
<pathelement location="${el.api.jar}"/>
<pathelement location="${jsf.api.jar}"/>
<pathelement location="${lib.dir}/jboss-seam.jar"/>
</path>

<target name="clean">
<delete dir="${dest.dir}"/>
<mkdir dir="${dest.dir}"/><code>
</code></target>

<target name="compileAll" depends="clean">
<javac compiler="modern" source="1.5" destdir="${dest.dir}">
<classpath refid="compile.classpath"/>
<src path="${web_module.src}"/>
<src path="${libs_modules.src}"/>
</javac>
</target>

<target name="createBeansJar" depends="clean">
<mkdir dir="${dest.dir}/beans"/>
<javac compiler="modern" source="1.5" destdir="${dest.dir}/beans">
<classpath location="${lib.dir}/jsf-api.jar"/>
<classpath location="${lib.dir}/jboss-seam.jar"/>
<classpath location="${lib.dir}/javaee.jar"/>
<classpath location="${lib.dir}/hibernate3.jar"/>
<classpath location="${lib.dir}/hibernate-annotations.jar"/>
<src path="${beans_module.src}"/>
</javac>
<copy file="${beans_module.src}/seam.properties" todir="${dest.dir}/beans"/>

<jar destfile="${dest.dir}/jsf.jar" basedir="${dest.dir}/beans">
<metainf dir="${beans_module.src}/META-INF">
<include name="**/*.*"/>
</metainf>
</jar>
</target>

<target name="createDeployBeansJar">
<mkdir dir="${dest.dir}/beans"/>
<javac compiler="modern" source="1.5" destdir="${dest.dir}/beans">
<classpath location="${lib.dir}/javaee.jar"/>
<src path="${beans_module.src}"/>
</javac>
<jar destfile="${dest.dir}/jsf.jar" basedir="${dest.dir}/beans">
<metainf dir="${beans_module.src}/META-INF">
<include name="**/*.*"/>
</metainf>
</jar>
<copy file="${dest.dir}/jsf.jar" todir="${jboss.deploy.dir}"/>
</target>

<target name="createWebWar" depends="createBeansJar">
<mkdir dir="${dest.dir}/web"/>
<war destfile="${dest.dir}/jsf.war" webxml="${web_module.src}/../WEB-INF/web.xml">

<fileset dir="${web_module.src}/../resources"/>

<webinf dir="${web_module.src}/../WEB-INF">
<include name="**/*.*"/>
<exclude name="**/web.xml"/>
</webinf>
<metainf dir="${web_module.src}/../META-INF">
<include name="**/*.*"/>
</metainf>
<lib dir="${lib.dir}">
<include name="jboss-seam-ui.jar"/>
<include name="jsf-facelets.jar"/>
<include name="commons-beanutils.jar"/>
</lib>
</war>
</target>

<target name="createEar" depends="createWebWar">
<copy file="${lib.dir}/jboss-seam.jar" todir="${dest.dir}"/>
<copy file="${lib.dir}/jboss-el.jar" todir="${dest.dir}"/>
<ear destfile="${dest.dir}/jsf.ear" appxml="META-INF/application.xml">

<fileset dir="${dest.dir}" includes="*.jar, *.war"/>
</ear>
</target>

<target name="deployDevelopmentEar" depends="createEar">
<copy file="${dest.dir}/jsf.ear" todir="${jboss.deploy.dir}"/>
</target>

</project>

build.properties

web_module.src=web/src
beans_module.src=beans
libs_modules.src=libs/src

dest.dir=dest
results=results
lib.dir=C:/JSFExample/supportingJars

jboss.deploy.dir=C:/jboss-4.2.3.GA/server/default/deploy
jboss.lib.dir=C:/jboss-4.2.3.GA/server/default

servlet.api.jar=C:/jboss-4.2.3.GA/server/default/lib/servlet-api.jar
jsp.api.jar=C:/jboss-4.2.3.GA/server/default/lib/jsp-api.jar
el.api.jar=C:/jboss-4.2.3.GA/server/default/lib/el-api.jar
jsf.api.jar=${lib.dir}/jsf-api.jar

jsf.lib.dir=${jsf.dir}/lib
jstl.lib.dir=${tomcat.dir}/lib

jsf.libs=jsf-api.jar,jsf-impl.jar
jstl.libs=jstl.jar,standard.jar

Run the build script. It should run successfully and copy a file called jsf.ear to your JBoss deploy directory.

While In this directory we need to create a datasource so we can connect to the database.
Create the following file and call it Oracle-ds.xml

Oracle-ds.xml

< ?xml version="1.0" encoding="UTF-8"?>
<datasources>
<local -tx-datasource>
<jndi -name>OracleDS</jndi>
<connection -url>jdbc:oracle:thin:@[URL]</connection>

<driver -class>oracle.jdbc.driver.OracleDriver</driver>
<user -name>[USER]</user>
<password>[PASSWORD]</password>

<exception -sorter-class-name>org.jboss.resource.adapter.jdbc.vendor.OracleExceptionSorter
</exception>

<metadata>
<type -mapping>Oracle9i</type>
</metadata>
</local>

</datasources>

replace [USER] with the username for your database, [PASSWORD] with the password and [URL] with the correct database URL.

Testing

Once complete we are good to go.
Start the JBoss and navigate to http://localhost:8080/web

you should see

fill in the details and press the button and you will see the results table:

This concludes this example I hope it helps you. This is the first time I have attempted to write a tutorial like this so please let me know if it was helpful, constructive criticism’s is also welcome.

EJB3, Seam, Tutorial , , , , , , , , , , , , ,