Cloning a VirtualBox openSUSE image

I recently tried to clone an openSUSE VirtualBox image. I thought it would be as simple as just copying the virtual disk image (vdi) file and create a new virtual machine based on it. So that’s what i did. But when i tried to couple the new hard disk file to the virtual machine VirtualBox gave me the following error:

Apparently a new virtual disk image in VirtualBox needs a unique identifier key. So after some research i found out it’s possible to make a clone of a hard disk that has a different unique identifier. But unfortunately that’s not all of the story. You have to make some additional changes to the clone to make it all work. So here are the steps you need to follow to make a successful clone.

1. Clone the hard disk

To clone the hard disk open a terminal window and issue the following command:

VBoxManage clonevdi <original>.vdi <clone>.vdi

2. Create a new Virtual Machine

Now create a new virtual machine with basically the same settings as the original virtual machine and couple it to the new cloned virtual disk image. As this image has been given a new unique identifier, you should have no problem registering it now.

3. Alter the hard disk identifiers

Having cloned the virtual disk image, i thought i was ready to roll. But on startup i encountered the following problem:

Apparently part of the UUID of the virtual disk image is used to identify the hard disk on startup. We have to change these references to their appropriate new ids.

3a. Startup in rescue mode

To startup the openSUSE vm in rescue mode insert the iso file you used for installation in the CD Drive of the vm. Upon startup select the Rescue System option from the menu.

3b. Mount the hard disk

Login as root and mount the hard disk (on my system this was /dev/sda2, this could be different on your system) via the following command:

mount /dev/sda2 /mnt

3c. Alter the identifiers

Now first find out what the new identifier of the hard disks should be. Issue the following command

hdparm -i /dev/sda

Note the identifier called SerialNo. This is the one you need. On my system it was VBa79c17fb-f28bb7c1.
Now there are 2 files you need to alter. First edit the file /mnt/etc/fstab and alter all the identifiers between /dev/disk/by-id/ata-VBOX_HARDDISK_ and -partx with the new identifier.
Next make corresponding changes to the file /mnt/boot/grub/menu.lst.
After this you can reboot the system

shutdown now -r

4. Repair your network settings

If you made no typos, openSUSE should start up with no problems. There’s one piece of configuration to do though. The clone has messed up the network configuration. This can be easily repaired via the YaST GUI tool. Open it and select Network Devices > Network Settings. You should see 2 Ethernet Controllers. One of them is not configured. Configure this controller with default settings and delete the other one. Now your clone is ready for use.

Using a nested property model in Apache Wicket for money validation

For one of my recent projects i needed to make sure that money fields were correctly translated from screen to database and vice versa.

The money fields are implemented as BigDecimal properties in the JPA model. The default Wicket behavior on input is to allow both the “.” and “,” characters and according to the active Locale to ignore the one that doesn’t correspond to the decimal seperator. This means that when the active Locale is the Dutch one (which uses the comma as the decimal seperator) and a user accidently enters €12.35 as the amount. It gets stored as €1235. This is very dangerous behavior.

The way we wanted the application to behave is to validate the input according to the Session Local and to disallow the entering of the wrong decimal seperator. There are probably a couple of way to do this, but the one i used involved using a custom validator and a nested property model.

Nested property model

The nested property model nests a BigDecimal model into a String model. Here’s the code.

package org.example.view.common;

import java.math.BigDecimal;
import org.apache.wicket.model.IModel;
import org.example.util.BigDecimalParser;
import org.example.view.format.BigDecimalToMoneySessionFormat;

public class DefaultMoneyModel implements IModel {

  private static final long serialVersionUID = 1479000955482442517L;
  private final IModel mNestedModel;

  public DefaultMoneyModel(IModel nestedModel) {
    mNestedModel = nestedModel;
  }

  public String getObject() {
    BigDecimal value = mNestedModel.getObject();
    // convert BigDecimal to String
    return BigDecimalToMoneySessionFormat.format(value);
  }

  public void setObject(String object) {
    // convert String to Bigdecimal
    BigDecimal value = BigDecimalParser.parseBigDecimal(object, getSessionLocale());
    mNestedModel.setObject(value);
  }

  public void detach() {
    mNestedModel.detach();
  }

  private Locale getSessionLocale() {
    // code to get the current Session Locale, omitted for brevity
    ....
  }
}

Custom validator

The custom validator i wrote first tries to parse the String to a BigDecimal just like Apache Wicket does and adds an extra check for the decimal separator.

package com.redwood.exchange.view.validator;

import java.math.BigDecimal;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.wicket.validation.IValidatable;
import org.apache.wicket.validation.validator.AbstractValidator;
import org.example.util.BigDecimalParser;

public class BigDecimalMoneyValidator extends AbstractValidator {

  private static final long serialVersionUID = 5638024896256902410L;

  @Override
  protected void onValidate(IValidatable validatable) {
    try {
      // First check that a normal conversion works
      @SuppressWarnings("unused")
      BigDecimal bigDecimal = BigDecimalParser.parseBigDecimal(
        validatable.getValue(), getSessionLocale());

      // Now check if the only non digit character is the decimal sign
      // corresponding to the locale and that it appears only once.
      checkDecimalSeparator(validatable.getValue(), getSessionLocale());
    } catch (Exception e) {
      error(validatable);
    }
  }

  private void checkDecimalSeparator(String value, Locale sessionLocale) {
    char decimalSeperator = new DecimalFormatSymbols(sessionLocale)
      .getDecimalSeparator();

    Pattern p = Pattern
      .compile("^\\d+(\\" + decimalSeperator + "\\d{2})?$");
    Matcher m = p.matcher(value);
    if (!m.find()) {
      throw new NumberFormatException("Invalid price.");
    }
  }

  private Locale getSessionLocale() {
    // code to get the current Session Locale, omitted for brevity
    ....
  }
}

Utility classes and resources

I need a parser class for parsing a String, i.e. user input, to a BigDecimal and a formatter class for presentation of the BigDecimal as a money field on the web page. Both should take the current Locale into account. Here’s the code for both classes.

package com.redwood.exchange.util;

import java.math.BigDecimal;
import java.util.Locale;
import org.apache.wicket.util.convert.converters.BigDecimalConverter;

public class BigDecimalParser {

  public static BigDecimal parseBigDecimal(String s, Locale locale) {
    BigDecimalConverter converter = new BigDecimalConverter();
    BigDecimal bigDecimal = (BigDecimal) converter.convertToObject(s,
      locale);
    return bigDecimal;
  }
}
package org.example.view.format;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Locale;

public class BigDecimalToMoneySessionFormat {

  public static String format(BigDecimal decimal, Locale locale) {
    if (decimal == null) {
      return "";
    }
    else {
      DecimalFormat decimalFormat = new DecimalFormat();
      decimalFormat.setMinimumFractionDigits(2);
      decimalFormat.setDecimalFormatSymbols(DecimalFormatSymbols
        .getInstance(locale));
      return decimalFormat.format(decimal);
    }
  }
}

Finally for displaying an error message i added a WicketApplication.properties file on the org.example.view level

BigDecimalMoneyValidator=The entered value is not a valid ${label}.

Adding it all to a Wicket Panel

Now that we have all the necessary code, let’s use it on a Wicket panel. Assume that we have a JPA entity class named Order with a property called price, which is a BigDecimal.

entering money

The money field is put on a html page corresponding to a Wicket panel via the following snippet:

  <span><div wicket:id="price" /></span>

The following code snippet from the corresponding Wicket Panel class will couple the component to the nested model and add the custom validator.

  TextField<String> priceField = new TextField<String>(
    "price", new DefaultMoneyModel(new PropertyModel<BigDecimal>(
    order, "price")));
  priceField.add(new BigDecimalMoneyValidator());

displaying money

The money field is put on a html page corresponding to a Wicket panel via the following snippet (in this case it’s put in a html table):

  <td wicket:id="price">EUR 12.95</td>

Now the following code in the corresponding Wicket panel class will couple the component to the nested model for use in a DataView.

  final DataView<Order> dataView = new DataView<Order>(..,..) {

    private static final long serialVersionUID = -6981777123077670308L;

    @Override
    public void populateItem(final Item<JPAEntity> item) {
      ..
      item.add(new Label("price", BigDecimalToMoneySessionFormat.format(item
        .getModelObject().getPrice())));
    }
  };

More info

For more info on using nested property models check out this excellent blog by Jeremy Tomerson.

Autosuggesting in Seam

Today I implemented an autosuggest textfield in a search page. Although technically not a Seam but a Richfaces feature, I baked it into a default Seam page. I added it to a default Seam search page backed up by an EntityQuery<E> subclass. Below you’ll find a screenshot of the Search Form containing the autosuggest field. It’s a search form for querying the collection of golfers in the database.

Autosuggest Search Form

Let’s take a walk through the most important code to get this example working.

Model classes

The golfer data in the database is contained in 2 tables MEMBER and GOLFER. These are mapped to 2 JPA classes called org.open18.model.Member and org.open18.model.Golfer respectively.

org.open18.model.Member

package org.open18.model;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;

@Entity
@Inheritance(strategy = InheritanceType.JOINED)
@Table(name = "MEMBER", uniqueConstraints = { @UniqueConstraint(columnNames = "username") })
public abstract class Member implements Serializable {
	private Long id;
	private String userName;

	@Id
	@GeneratedValue
	public Long getId() {
		return id;
	}

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

	@Column(name = "username", nullable = false)
	public String getUserName() {
		return userName;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

}

org.open18.model.Golfer

package org.open18.model;

package org.open18.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.jboss.seam.annotations.Name;

@Entity
@PrimaryKeyJoinColumn(name = "MEMBER_ID")
@Table(name = "GOLFER")
@Name("golfer")
public class Golfer extends Member {
	private String firstName;
	private String lastName;
	private Date dateJoined;

	@Column(name = "last_name", nullable = false)
	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	@Column(name = "first_name", nullable = false)
	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	@Transient
	public String getName() {
		return (firstName + ' ' + lastName);
	}

	@Temporal(TemporalType.TIMESTAMP)
	@Column(name = "joined", nullable = false, updatable = false)
	public Date getDateJoined() {
		return dateJoined;
	}

	public void setDateJoined(Date dateJoined) {
		this.dateJoined = dateJoined;
	}

}

Action class

The page is backed up by the action class org.open18.action.GolferList. This class contains an action method for providing the data for the autosuggest field.

org.open18.action.GolferList

package org.open18.action;

import java.util.Arrays;
import java.util.List;

import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.framework.EntityQuery;
import org.open18.model.Golfer;

@Name("golferList")
public class GolferList extends EntityQuery<Golfer> {
	private static final String EJBQL = "select golfer from Golfer golfer";

	private static final String[] RESTRICTIONS = { "lower(golfer.lastName) like lower(concat(#{golferList.golfer.lastName},'%'))", };

	private Golfer golfer = new Golfer();

	public GolferList() {
		setEjbql(EJBQL);
		setRestrictionExpressionStrings(Arrays.asList(RESTRICTIONS));
		setMaxResults(25);
	}

	public Golfer getGolfer() {
		return golfer;
	}

	@SuppressWarnings("unchecked")
	@Transactional
	public List<Golfer> autocomplete(Object suggest) {

		String pref = (String) suggest + "%";

		List<Golfer> golferSuggestList;
		golferSuggestList = getEntityManager()
				.createQuery(
						"select golfer from Golfer golfer where golfer.lastName like :golfer")
				.setParameter("golfer", pref).getResultList();

		return golferSuggestList;

	}
}

Page descriptor

The page descriptor contains page parameters for storing some search and navigation criteria.

golferList.page.xml

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

  <param name="from" />

  <param name="lastName" value="#{golferList.golfer.lastName}" />

</page>

Page

Finally the page itself. After the code, I’ll explain some of the elements/attributes on the page.

GolferList.xhtml

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<ui:composition xmlns="http://www.w3.org/1999/xhtml"
  xmlns:s="http://jboss.com/products/seam/taglib"
  xmlns:ui="http://java.sun.com/jsf/facelets"
  xmlns:f="http://java.sun.com/jsf/core"
  xmlns:h="http://java.sun.com/jsf/html"
  xmlns:a="http://richfaces.org/a4j"
  xmlns:rich="http://richfaces.org/rich"
  template="layout/template.xhtml">

  <ui:define name="body">

    <h:form id="courseSearch" styleClass="edit">

      <rich:simpleTogglePanel label="Course Search Filter"
        switchType="ajax">

        <s:decorate template="layout/display.xhtml">
          <ui:define name="label">Last Name</ui:define>
          <h:inputText id="lastName"
            value="#{golferList.golfer.lastName}">
          </h:inputText>
          <rich:suggestionbox ajaxSingle="true" for="lastName"
            var="_golfer" minChars="2"
            suggestionAction="#{golferList.autocomplete}">
            <h:column>
              <h:outputText value="#{_golfer.lastName}" />
            </h:column>
            <a:support event="onselect" reRender="golferSearchResults" />
          </rich:suggestionbox>
        </s:decorate>

      </rich:simpleTogglePanel>

      <div class="actionButtons">
        <h:commandButton id="search"
          value="Search" action="/golferList.xhtml">
        </h:commandButton> <s:button
          id="reset" value="Reset" includePageParams="false" />
      </div>
    </h:form>

    <rich:panel id="golferSearchResults">
      <f:facet name="header">Golfers</f:facet>

      <div class="results">
        <h:outputText
          value="There are no golfers registered."
          rendered="#{empty golferList.resultList}" />
        <rich:dataTable
          id="golferList" var="_golfer" value="#{golferList.resultList}"
          rendered="#{not empty golferList.resultList}">
          <h:column>
            <f:facet name="header">Username</f:facet>
            <s:link id="view" value="#{_golfer.userName}"
              view="/profile.xhtml" propagation="none">
              <f:param name="golferId" value="#{_golfer.id}" />
            </s:link>
          </h:column>
          <h:column>
            <f:facet name="header">Name</f:facet>
            #{_golfer.name}
          </h:column>
          <h:column>
            <f:facet name="header">Date Joined</f:facet>
            <h:outputText value="#{_golfer.dateJoined}">
              <s:convertDateTime pattern="MMMM dd, yyyy" />
            </h:outputText>
          </h:column>
        </rich:dataTable>
      </div>

    </rich:panel>

  </ui:define>

</ui:composition>
  • layout/template.xhtml, layout/display.xhtml: these are standard facelets generated by seam-gen;
  • rich:suggestionbox: this element will take care of the suggestion data for the input textfield
  • for=”lastName”: points to the if of the h:inputText component for which the autosuggest data will be shown;
  • minChars=”2″: After 2 characters are entered the suggestion list will appear
  • suggestionAction=”#{golferList.autocomplete}”: Calls the autocomplete action method on the GolferList action class, which will return a list of golfers;
  • h:column: The result will be shown as one column and will contain a suggestion list of the golfers last names via the h:outputText component;
  • a:support: This element takes care of rerendering the golferSearchResults panel when a user has selected a last name in the suggestion list.
  • Performance note

    Hitting the database everytime a user is rumbling in the autosuggest field is probably not the best solution. As an alternative you could create a page scoped factory which captures the autosuggest data in a List<Golfer> variable. This factory could then be injected into the golferList component. In this case the autosuggest query will only be executed once per page visit. See the scriptlets below.

    components.xml

    	....
    	<framework:entity-query name="golferSuggestQuery"
    		ejbql="select golfer from Golfer golfer" >
    	</framework:entity-query>
    	<factory name="golferSuggest" value="#{golferSuggestQuery.resultList}" scope="page" />
    	....
    

    org.open18.action.GolferList

    @Name("golferList")
    public class GolferList extends EntityQuery<Golfer> {
    	@In(create=true,value="golferSuggest")
    	List<Golfer> golferSuggest;
    	....
    	public List<Golfer> autocomplete(Object suggest) {
    		String pref = (String) suggest;
    
    		List<Golfer> golferSuggestList = new ArrayList<Golfer>(0);
    
    		for (Golfer g : golferSuggest) {
    			if (g.getLastName().startsWith(pref)) {
    				golferSuggestList.add(g);
    			}
    		}
    
    		return golferSuggestList;
    	}
    	....
    }
    

    Adding Maven to seam-gen

    Recently I changed a seam-gen generated project into a maven project. It took me a while to get it all up and running. In this blog I’ll try to summarize all the steps needed to convert a seam-gen app (the open18 app I created in the previous blogs) into a full blown Maven project called open18_mvn2. I took the liberty to stretch the Maven pom to support deployment to glassfish as well as jboss.

    1. Prerequisites

    Above is the list with prerequisites. Most of them are taken care of when you’ve followed all the steps in the previous blog series “Seam-gen on glassfish”.

    • You’ve created the seam-gen project open18;
    • You’ll have a mysql database up and running in which resides the open18 catalog with application data.
    • You’ll have a glassfish installation available in ~/opt/glassfishv2;
      • The necessary hibernate and slf4j/log4j libraries and the mysql-connector-java-5.1.12-bin.jar file are all available in the <glassfish_home>/domains/domain1/lib/ext directory. This can be done via the ant task gf-prepare of the seam-gen app;
      • A connection pool (open18Pool) together with a datasource (open18Datasource) for connecting to the mysql open18 catalog is available in the glassfish server. This datasource can be installed on the server via the ant task gf-deploy-datasource of the seam-gen app;
    • For this blog I added a jboss server installation and put it in ~/opt/jboss-5.1.0.GA;
      • See the next section for installation details;
      • The mysql driver mysql-connector-java-5.1.12-bin.jar is available in the <jboss_home>/server/default/lib directory;
      • A datasource file open18-ds.xml is available in the jboss server directory <jboss_home>/server/default/deploy for connecting to the mysql open18 catalog. This datasource can be installed on the server via the ant task datasource of the seam-gen app.

    2. Installing JBoss server

    • First download the latest JBoss 5 release here;
    • Unzip the zipfile into your ~/opt folder;
    • For hot deployment to work you need to alter the file <jboss_home>/server/default/conf/bootstrap/profile.xml. Uncomment the following line
              <value>WEB-INF/dev</value>
    
    • To prevent any memory issues from occurring make sure you start jboss with the following vm arguments (you can do this by adding the jboss server to your Netbeans servers):
    -Xms128m -Xmx512m -Dsun.rmi.dgc.client.gcInterval=3600000 -Dsun.rmi.dgc.server.gcInterval=3600000 -XX:+UseConcMarkSweepGC -XX:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=512m -Xverify:none
    

    3. Project structure

    Next we’ll setup the maven project structure, wherein we will copy the necessary files from the seam-gen app.

    open18_mvn2 project structure

    • src
      • main
        • hot: Folder for the hot deployable sources (i.e. the action classes). In the seam-gen app this is the src/hot folder;
        • java: Folder for the ordinary java sources (eg. the model classes). In the seam-gen app this is the src/main folder;
        • resources: All the resource files. In the seam-gen app this is the resources folder without the WEB-INF folder. Of all the multiple environment files, I only added the -dev files and renamed them by dropping the –dev extension. For example, i’ve renamed the components-dev.properties to components.properties.;
        • webap: The web-app folder containing all the view files. In the seam-gen app this is the view folder combined with the resources/WEB-INF folder;
      • test
        • bootstrap: The bootstrap files needed for testing with jboss-embedded. In the seam-gen app this is the bootstrap folder;
        • java: Folder for the test classes. In the seam-gen app this is the src/test folder;
        • resources: Resource files for testing. In the seam-gen app these are the resources/META-INF/persistence-test.xml, resources/components-test.properties and resources/import-test.sql files. I’ve renamed them by dropping off the -test extension.

    4. pom.xml

    This is the pom file with which it’ll be possible to eg. test and explode the application to glassfish and jboss. I’ve highlighted the lines which I will comment upon in this section.

    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    
        <modelVersion>4.0.0</modelVersion>
        <groupId>org.open18</groupId>
        <artifactId>open18_mvn2</artifactId>
        <packaging>war</packaging>
        <version>1.0-SNAPSHOT</version>
        <name>open18_mvn2 JEE5 Webapp</name>
        <url>http://maven.apache.org</url>
    
        <repositories>
            <repository>
                <id>jboss</id>
                <name>JBoss Release Repository</name>
                <url>http://repository.jboss.org/maven2</url>
            </repository>
        </repositories>
    
        <pluginRepositories>
            <pluginRepository>
                <id>ctpjava</id>
                <name>CTP Public Repository</name>
                <url>http://ctpjava.googlecode.com/svn/trunk/repository</url>
            </pluginRepository>
            <pluginRepository>
                <id>repository.jboss.org</id>
                <name>JBoss Repository</name>
                <url>http://repository.jboss.org/maven2</url>
            </pluginRepository>
            <pluginRepository>
                <id>maven.java.net</id>
                <name>Java.net Maven2 Repository</name>
                <url>http://download.java.net/maven/2</url>
            </pluginRepository>
        </pluginRepositories>
    
        <dependencies>
    
            <!-- *************** Build Dependencies *************** -->
    
            <dependency>
                <groupId>org.jboss.seam.embedded</groupId>
                <artifactId>hibernate-all</artifactId>
                <version>${jboss.embedded.version}</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- *************** Test Dependencies *************** -->
    
            <dependency>
                <groupId>org.testng</groupId>
                <artifactId>testng</artifactId>
                <version>5.8</version>
                <classifier>jdk15</classifier>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>junit</groupId>
                        <artifactId>junit</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam.embedded</groupId>
                <artifactId>jboss-embedded-all</artifactId>
                <version>${jboss.embedded.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam.embedded</groupId>
                <artifactId>jboss-embedded-api</artifactId>
                <version>${jboss.embedded.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam.embedded</groupId>
                <artifactId>thirdparty-all</artifactId>
                <version>${jboss.embedded.version}</version>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>javax.faces</groupId>
                <artifactId>jsf-api</artifactId>
                <version>${javax.faces.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.faces</groupId>
                <artifactId>jsf-impl</artifactId>
                <version>${javax.faces.version}</version>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>javax.el</groupId>
                <artifactId>el-api</artifactId>
                <version>1.2</version>
                <scope>provided</scope>
            </dependency>
    
            <!-- *************** Seam Dependencies *************** -->
    
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam</artifactId>
                <version>${jboss.seam.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.el</groupId>
                        <artifactId>el-api</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam-ui</artifactId>
                <version>${jboss.seam.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.el</groupId>
                        <artifactId>el-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>org.jboss.seam</groupId>
                        <artifactId>jboss-seam-jul</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>commons-beanutils</groupId>
                        <artifactId>commons-beanutils</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam-ioc</artifactId>
                <version>${jboss.seam.version}</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam-debug</artifactId>
                <version>${jboss.seam.version}</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam-mail</artifactId>
                <version>${jboss.seam.version}</version>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam-remoting</artifactId>
                <version>${jboss.seam.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>dom4j</groupId>
                        <artifactId>dom4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.jboss.seam</groupId>
                <artifactId>jboss-seam-pdf</artifactId>
                <version>${jboss.seam.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>com.lowagie</groupId>
                        <artifactId>itext</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>com.lowagie</groupId>
                        <artifactId>itext-rtf</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!-- *************** RichFaces Dependency *************** -->
    
            <dependency>
                <groupId>org.richfaces.ui</groupId>
                <artifactId>richfaces-ui</artifactId>
                <version>${jboss.richfaces.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-collections</groupId>
                        <artifactId>commons-collections</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.richfaces.framework</groupId>
                <artifactId>richfaces-api</artifactId>
                <version>${jboss.richfaces.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-collections</groupId>
                        <artifactId>commons-collections</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.richfaces.framework</groupId>
                <artifactId>richfaces-impl</artifactId>
                <version>${jboss.richfaces.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.richfaces.samples</groupId>
                <artifactId>glassX</artifactId>
                <version>${jboss.richfaces.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>jstl</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>nekohtml</groupId>
                        <artifactId>nekohtml</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!-- *************** Drools / jBPM Dependency *************** -->
    
            <dependency>
                <groupId>org.drools</groupId>
                <artifactId>drools-compiler</artifactId>
                <version>${drools.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>xerces</groupId>
                        <artifactId>xercesImpl</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>xml-apis</groupId>
                        <artifactId>xml-apis</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>janino</groupId>
                        <artifactId>janino</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.jbpm</groupId>
                <artifactId>jbpm-jpdl</artifactId>
                <version>${jboss.jbpm-jpdl.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
    
            <!-- *************** Facelets Dependency *************** -->
    
            <dependency>
                <groupId>com.sun.facelets</groupId>
                <artifactId>jsf-facelets</artifactId>
                <version>1.1.15</version>
            </dependency>
        </dependencies>
    
        <build>
            <finalName>${project.artifactId}</finalName>
            <resources>
                <resource>
                    <directory>src/main/resources</directory>
                    <includes>
                        <include>META-INF/orm.xml</include>
                        <include>META-INF/persistence.xml</include>
                        <include>messages*.properties</include>
                        <include>seam.properties</include>
                        <include>components.properties</include>
                        <include>security.drl</include>
                    </includes>
                </resource>
            </resources>
            <testResources>
                <testResource>
                    <directory>src/test/resources</directory>
                </testResource>
                <testResource>
                    <directory>src/test/bootstrap</directory>
                </testResource>
                <testResource>
                    <directory>src/main/webapp</directory>
                </testResource>
            </testResources>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>${java.source.version}</source>
                        <target>${java.source.version}</target>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>build-helper-maven-plugin</artifactId>
                    <executions>
                        <execution>
                            <id>add-source</id>
                            <phase>generate-sources</phase>
                            <goals>
                                <goal>add-source</goal>
                            </goals>
                            <configuration>
                                <sources>
                                    <source>${java.source.hotdeploy}</source>
                                </sources>
                            </configuration>
                        </execution>
                    </executions>
                </plugin>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <configuration>
                        <childDelegation>true</childDelegation>
                        <useSystemClassLoader>true</useSystemClassLoader>
                        <argLine>
                            -Dsun.lang.ClassLoader.allowArraySyntax=true
                        </argLine>
                    </configuration>
                </plugin>
                <plugin>
                    <!-- run with 'mvn cli:execute-phase' and use 'hot' -->
                    <groupId>org.twdata.maven</groupId>
                    <artifactId>maven-cli-plugin</artifactId>
                    <configuration>
                        <userAliases>
                            <hot>hotdeploy:exploded -o -Pjboss.local</hot>
                        </userAliases>
                    </configuration>
                </plugin>
                <plugin>
                    <artifactId>maven-antrun-plugin</artifactId>
                    <version>1.3</version>
                    <executions>
                        <execution>
                            <id>copy-test-persistence</id>
                            <phase>process-test-resources</phase>
                            <configuration>
                                <tasks>
                                    <!--backup the "proper" persistence.xml-->
                                    <copy file="${project.build.outputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml.bck"/>
                                    <!--replace the "build" persistence.xml with the "test" version-->
                                    <copy file="${project.build.testOutputDirectory}/META-INF/persistence.xml" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                                </tasks>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                        <execution>
                            <id>restore-persistence</id>
                            <phase>prepare-package</phase>
                            <configuration>
                                <tasks>
                                    <!--restore the "build" persistence.xml-->
                                    <move file="${project.build.outputDirectory}/META-INF/persistence.xml.bck" tofile="${project.build.outputDirectory}/META-INF/persistence.xml" overwrite="true"/>
                                </tasks>
                            </configuration>
                            <goals>
                                <goal>run</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
        <profiles>
            <profile>
                <id>jboss.local</id>
                <build>
                    <defaultGoal>hotdeploy:exploded</defaultGoal>
                    <plugins>
                        <plugin>
                            <groupId>com.ctp.seam.maven</groupId>
                            <artifactId>maven-hotdeploy-plugin</artifactId>
                            <version>0.3.1</version>
                            <configuration>
                                <source>${java.source.version}</source>
                                <target>${java.source.version}</target>
                                <sourceDirectory>${java.source.hotdeploy}</sourceDirectory>
                                <deployDirectory>
                                    ${directory.deploy.jboss}/${build.finalName}.${project.packaging}
                                </deployDirectory>
                            </configuration>
                        </plugin>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-clean-plugin</artifactId>
                            <configuration>
                                <filesets>
                                    <fileset>
                                        <directory>${directory.deploy.jboss}</directory>
                                        <includes>
                                            <include>**/${build.finalName}.${project.packaging}*</include>
                                        </includes>
                                        <followSymlinks>false</followSymlinks>
                                    </fileset>
                                </filesets>
                            </configuration>
                        </plugin>
                    </plugins>
                </build>
                <properties>
                    <directory.deploy.jboss>~/Programming/opt/jboss-5.1.0.GA/server/default/deploy</directory.deploy.jboss>
                </properties>
            </profile>
    
            <profile>
                <id>glassfish.local</id>
                <build>
                    <plugins>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-war-plugin</artifactId>
                            <executions>
                                <execution>
                                    <id>Prepare WAR</id>
                                    <phase>prepare-package</phase>
                                    <goals>
                                        <goal>exploded</goal>
                                    </goals>
                                </execution>
                            </executions>
                        </plugin>
                        <plugin>
                            <groupId>org.codehaus.mojo</groupId>
                            <artifactId>exec-maven-plugin</artifactId>
                            <version>1.1</version>
                            <executions>
                                <execution>
                                    <id>explode to glassfish</id>
                                    <phase>prepare-package</phase>
                                    <goals>
                                        <goal>exec</goal>
                                    </goals>
                                    <configuration>
                                        <executable>${glassfish.home}/bin/asadmin</executable>
                                        <arguments>
                                            <argument>deploy</argument>
                                            <argument>${project.build.directory}/${project.build.finalName}</argument>
                                        </arguments>
                                    </configuration>
                                </execution>
                                <execution>
                                    <id>clean and unexplode</id>
                                    <phase>clean</phase>
                                    <goals>
                                        <goal>exec</goal>
                                    </goals>
                                    <configuration>
                                        <executable>${glassfish.home}/bin/asadmin</executable>
                                        <arguments>
                                            <argument>undeploy</argument>
                                            <argument>${project.build.finalName}</argument>
                                        </arguments>
                                    </configuration>
                                </execution>
                            </executions>
                        </plugin>
                    </plugins>
                </build>
                <properties>
                    <glassfish.home>~/Programming/opt/glassfishv2</glassfish.home>
                </properties>
            </profile>
        </profiles>
    
        <properties>
            <java.source.version>1.5</java.source.version>
            <java.source.hotdeploy>${basedir}/src/main/hot</java.source.hotdeploy>
            <jboss.seam.version>2.2.1.CR1</jboss.seam.version>
            <jboss.richfaces.version>3.3.3.CR1</jboss.richfaces.version>
            <jboss.jbpm-jpdl.version>3.2.3</jboss.jbpm-jpdl.version>
            <jboss.embedded.version>beta3.SP12</jboss.embedded.version>
            <drools.version>5.0.1</drools.version>
            <javax.faces.version>1.2_13</javax.faces.version>
        </properties>
    
    </project>
    

    line 292-302: testResources

    For testing purposes, you need

    • the webapp directory: this includes for one the facelets and page desciptors;
    • the test resources directory: this  will contain files like persistence.xml and components.properties;
    • the test bootstrap directory: this will contain the bootstrap files needed for testing with jboss embedded server.

    line 330-340: maven-surefire-plugin

    Make sure to add this plugin configuration, or else the tests will surely fail.

    line 351-384: maven-antrun-plugin

    This is a little workaround. JBoss embedded unfortunately doesn’t see the persistence.xml file in the target/test-classes/META-INF directory. It has to be in the target/classes/META-INF directory. To work around this issue, we’ll make a backup of the persistence.xml in the classes tree and replace it with the test-classes version. After the testing phase we’ll put the backed up persistence.xml file back in place.

    line 390: jboss.local profile

    Profile for hot deployment and undeployment to jboss.

    line 425: directory.deploy.jboss

    Make sure to point this to the domain folder of your jboss installation.

    line 430: glassfish.local profile

    Profile for hot deployment and undeployment to glassfish.

    line 484: glassfish.home

    Make sure to point this to your glassfish installation.

    At this point your project in Netbeans should look like the picture below

    open_mvn2 project in Netbeans

    5. Test class example

    To check if testing (and especially persistence against the Hypersonic database) works correctly I’ve added the test class org.open18.test.CreateNewFacilityActionTest, which adds a new facility. Note that you have to be logged in first to be able to make any permanent changes:

    package org.open18.test;
    
    import java.util.List;
    
    import javax.faces.application.FacesMessage;
    
    import org.jboss.seam.faces.FacesMessages;
    import org.jboss.seam.mock.SeamTest;
    import org.testng.annotations.Test;
    
    public class CreateNewFacilityActionTest extends SeamTest {
    
        @Test(groups = { "level.integration", "speed.slow" })
        public void createNewFacilityTest() throws Exception {
    
            new FacesRequest("/login.xhtml") {
    
                @Override
                protected void updateModelValues() {
    
                    setValue("#{credentials.username}", "admin");
                }
    
                @Override
                protected void invokeApplication() {
                    Object outcome = invokeMethod("#{identity.login}");
                    assert outcome != null && outcome.equals("loggedIn");
                }
    
                @Override
                protected void renderResponse() throws Exception {
                       List<FacesMessage> messages = FacesMessages.instance()
                            .getCurrentGlobalMessages();
                    assert messages.size() == 1;
                    assert messages.get(0).getSeverity().equals(
                            FacesMessage.SEVERITY_INFO);
                    assert messages.get(0).getSummary().contains("admin");
                }
            }.run();
    
            new FacesRequest("/FacilityEdit.xhtml") {
    
                @Override
                protected void updateModelValues() {
                    setValue("#{facilityHome.instance.name}", "Eindhoven golf");
                    setValue("#{facilityHome.instance.type}", "PUBLIC");
                }
    
                @Override
                protected void invokeApplication() {
                    Object outcome = invokeMethod("#{facilityHome.persist}");
                    assert outcome != null && outcome.equals("persisted");
                }
    
                @Override
                protected void renderResponse() throws Exception {
                    List<FacesMessage> messages = FacesMessages.instance()
                            .getCurrentGlobalMessages();
                    assert messages.size() == 1;
                    assert messages.get(0).getSeverity().equals(
                            FacesMessage.SEVERITY_INFO);
                    assert(messages.get(0).getSummary().contains("Successfully created"));
    
                }
            }.run();
        }
    }
    

    This is the test suite file FacilityIntegrationActionTest.xml that goes along with it:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" >
    
    <suite name="RegisterAction Tests" verbose="2" parallel="false">
        <test name="RegisterAction Test">
            <classes>
                <class name="org.open18.test.RegisterGolferIntegrationActionTest" />
            </classes>
        </test>
        <test name="CreateNewFacility Test">
            <classes>
                <class name="org.open18.test.CreateNewFacilityActionTest" />
            </classes>
        </test>
    </suite>
    

    6. Other adjustments

    For simplicity I didn’t bother to convert the @ant-variable@ replacement in the component.properties file to Maven, so I just altered the file and put the correct hard-coded values in it.

    src/main/resources/components.properties

    jndiPattern=open18/#{ejbName}/local
    debug=true
    seamBootstrapsPu=false
    seamEmfRef=#{null}
    puJndiName=java:comp/env/open18/pu
    

    There are also a couple of files that need to be adjusted for the test goal to work.

    src/test/resources/META-INF/persistence.xml

    Make sure the following hibernate properties are set with the values shown below

             <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.JBossTransactionManagerLookup"/>
             <property name="jboss.entity.manager.factory.jndi.name"
                    value="java:/entityManager"/>
    

    bootstrap/deploy/hsqldb-ds.xml

    Because the generated JPA classes all contain a catalog named “OPEN18” you need to make sure that when Hibernate is going to create the tables in the Hypersonic database you’re connected to a schema by the same name. If you don’t do this, you’ll get a bunch of “invalid schema name OPEN18” messages when running the maven test goal. Add the following line below the displayed comment

          <!-- sql to call when connection is created -->
            <new-connection-sql>CREATE SCHEMA OPEN18 AUTHORIZATION DBA</new-connection-sql>
    

    7. Running the goals

    Now you can test your Maven seam-gen app.

    • Test (Netbeans context menu) will run the maven test goal;
    • Run (Netbeans context menu) and selecting a server will deploy the application;
    • Clean (Netbeans context menu) will run the maven clean goal. When you’ve set one of the profiles (via the Set Configuration context menu) it will also undeploy the application from the server;
    • Profile jboss.local (via Custom goals context menu) will hot deploy the application to jboss;
    • Goal prepare-package with profile glassfish.local (via Custom goals context menu) will hot deploy the application to glassfish.

    Seam-gen on Glassfish – part III

    In this last part we’re gonna create the seam-gen project, import it in NetBeans, deploy it and at the end we have a running application.

    1. Create the project

    Creating the skeleton of the seam-gen project is done by issuing the following seam-gen command

    $ ./seam create-project

    If you stuck to the defaults there will be a project directory called open18 in your home directory.

    2. Generate the JPA classes

    Next we’ll generate the JPA classes from the tables in the open18 database into the new project. Again this is done by a seam-gen command

    $ ./seam generate

    3. Import in NetBeans

    Now let’s import the project into NetBeans. It should look like the picture below

    open18 project

    The build targets for the NetBeans project are coupled to the default (i.e. JBoss) deploy targets. Let’s change them to their Glassfish counterparts. Right click on the open18 project and choose properties. Change the targets so they’ll look like this

    open 18 build properties

    4. Change build-glassfish.xml

    This is the build file that contains all the Glassfish specific Ant targets. One thing it does, is copying all the Hibernate files to the lib/ext folder of the Glassfish domain (Glassfish uses EclipseLink as its default JPA provider).

    We’re gonna make 2 changes to this file.

    First we’re gonna make sure that the slf4j/log4j libraries also get copied to the Glassfish domain. If you omit this step, the application won’t deploy properly. Add the following xml stanza to the gf-deploy-hibernate target in the glassfish-build.xml file so that the copy task which copies the libraries to Glassfish resembles the example below.

            <copy todir="${glassfish.domains.dir}/${glassfish.domain}/lib/ext" overwrite="true">
                <fileset dir="${basedir}/lib">
                    ....
                    <!-- Include slf4j/log4j JARs below -->
                    <include name="jcl-over-slf4j.jar"/>
                    <include name="slf4j-api.jar"/>
                    <include name="slf4j-log4j12.jar"/>
                    <include name="log4j.jar"/>
                </fileset>
            </copy>
    

    Next let’s fix a small bug in the deployment of the libraries to a Glassfish v2 server. If you don’t change this the asm.jar, asm-attrs.jar, cglib.jar and cglib-nodep.jar won’t be copied into the Glassfish domain.

    Alter the lines in the gf-deploy-hibernate target that check whether the Glassfish server is a version 2  or 3.

    <condition property="glassfish.v3" value="true" else="false">
        <available file="${glassfish.home}/glassfish" type="dir"/>
    </condition>
    <condition property="glassfish.domains.dir" value="${glassfish.home}/glassfish/domains" else="${glassfish.home}/domains">
        <istrue value="${glassfish.v3}"/>
    </condition>
    

    5. Prepare Glassfish for Hibernate

    Now let’s copy all those library files to the Glassfish domain. Right click the build.xml file in the NetBeans project and choose Run Target > gf-prepare.

    One last thing that needs to be taken care of, is fixing a bug in the Seam code regarding Hibernate and mysql. If you don’t do this, the facelet TeeList.xhtml (which corresponds with the Tee List menu item in the open18 application) will give you the following error message

    /TeeList.xhtml: javax.persistence.PersistenceException: org.hibernate.exception.DataException: could not execute query

    A nice workaround is provided here. In this workaround the flawed Seam component org.jboss.seam.persistence.persistenceProvider is overriden with the following Seam component (which has a higher precedence)

    package org.open18.persistence;
    
    import javax.annotation.PostConstruct;
    
    import org.jboss.seam.ScopeType;
    import org.jboss.seam.annotations.Install;
    import org.jboss.seam.annotations.Name;
    import org.jboss.seam.annotations.Scope;
    import org.jboss.seam.annotations.intercept.BypassInterceptors;
    import org.jboss.seam.log.Log;
    import org.jboss.seam.log.Logging;
    import org.jboss.seam.persistence.HibernatePersistenceProvider;
    
    /**
     * Addresses problem with HibernatePersistenceProvider by explicitly
     * calling HibernatePersistenceProvider.init() from
     * a PostConstruct method that actually gets called on postconstruct.
     *
     */
    @Name("org.jboss.seam.persistence.persistenceProvider")
    @Scope(ScopeType.STATELESS)
    @BypassInterceptors
    @Install(classDependencies={"org.hibernate.Session", "javax.persistence.EntityManager"})
    public class MoBetterHibernatePersistenceProvider extends HibernatePersistenceProvider {
    
        private static Log log = Logging.getLog(MoBetterHibernatePersistenceProvider.class);
    
        @PostConstruct
        public void init()
        {
           log.debug("MoBetterPersistenceProvider init");
           super.init();
        }
    
    }
    

    6. Deploy the application

    We’re almost there. Let’s deploy the application.

    • Right click the build.xml file and choose Run Target > gf-start to startup the Glassfish server;
    • Right click the open18 project and choose Clean and Build.

    7. Run the application

    Finally! You should be able to play with the open18 seam-gen app which runs on the following url: http://localhost:8080/open18.

    Seam-gen on Glassfish – part II

    In this second part we’re gonna setup an example database, prepare Glassfish for mysql and finally run seam setup to create the proper build.properties file for deployment to glassfish. I’m using the example schema provided by the excellent Seam in Action book by Manning. The setup scripts can be found in the zip file located here.

    To recap I’m using the following file structure as recommended in the Seam in Action book:

    • ~/lib: this will contain the mysql driver, which will be copied by seam-gen to the Glassfish server;
    • ~/opt: this already contains jboss-seam and the Glassfish server;
    • ~/open18: this will contain the seam-gen generated project.

    1. Installing the database schema

    First download the seaminaction-20090413.zip file and extract it in your home directory.

    Now login to the mysql database

    $ mysql -u <user_name> -p
    

    Now create a database and run the mysql scripts contained in the zip file

    sql> CREATE DATABASE open18;
    sql> USE open18;
    sql> source ~/seaminaction/etc/schema/open18-initial-schema-mysql.sql
    sql> source ~/seaminaction/etc/schema/open18-initial-seed-data-mysql.sql
    

    2. Download and install the mysql driver

    Next we’ll gonna provide seam-gen with a recent mysql driver

    • First, download the MySQL Connector/J zip file here.
    • Unzip this file and copy the mysql-connector-java-5.1.12-bin.jar to your ~/lib directory

    The jar will be picked up by seam-gen later on and copied to the Glassfish domain. You can also choose to copy the driver file directly to the Glassfish server by copying the jar file to <glassfish_home>/domain/domain1/lib/ext (assuming you’ll be deploying the seam-gen app to this default domain).

    3. Running seam-gen setup

    Now it’s time to run seam-gen setup. This will create the proper build.properties file needed for the creation the the NetBeans project later on.

    Switch to ~/opt/jboss-seam-2.2.1.CR1 and enter

    $ ./seam setup
    

    Answer the questions and verifiy the generated build.properties file in the ~/opt//jboss-seam-2.2.1.CR1/seam-gen directory. It should look like this:

    seam-gen build.properties

    Now all the hard work is done, in the last part of this blog series, we’re gonna create the seam-gen project and deploy it to our Glassfish domain!!

    Seam-gen on Glassfish – part I

    I recently managed to get a seam-gen app running on Glassfish. To share how this was done, i’ve divided this blog in 3 parts. This part will handle most of the prerequisite steps.

    The complete setup

    • Ubuntu 9.10
    • Mysql server 5.1
    • Glassfish v2.1.1
    • Jboss Seam 2.2.1.CR1
    • Netbeans 6.8
    • Ant 1.7.1
    • Subversion 1.6.5

    Jboss Seam and the Glassfish server will be installed under ~/opt.

    1. Install JDK 6

    First of all you need to install a recent JDK version

    $ sudo apt-get update
    $ sudo apt-get install sun-java6-jdk
    

    Note: if you’re using ubuntu10 you need to enable the “partner” repository first. This is done via the System > Administration > Software menu.

    2. Install Mysql Server 5.1

    Next we’re gonna install mysql server on ubuntu

    $sudo apt-get install mysql-server
    

    3. Install Subversion (optional)

    For versioning support we’ll be using subversion with http support

    $ sudo apt-get install subversion
    $ sudo apt-get install apache2
    $ sudo apt-get install libapache2-svn
    

    3a. Create repository

    Next we’re gonna create a svn repository

    $ sudo svnadmin create /var/svn/repos
    

    3b. Edit svn configuration

    Edit the subversion configuration

    $ sudo gedit /etc/apache2/mods-enabled/dav_svn.conf

    Alter this so that effectively when ignoring all comments it’ll look like this

    <Location /svn>
        DAV svn
        SVNPath /var/svn/repos
        AuthType Basic
        AuthName "Subversion Repository"
        AuthUserFile /etc/apache2/dav_svn.passwd
    </Location>

    3c. Create a svn user

    We need a user to connect to the svn repository

    $ sudo htpasswd -cm /etc/apache2/dav_svn.passwd <user_name>

    When prompted enter a pasword.

    3d. Restart Apache

    $ sudo /etc/init.d/apache2 restart

    3e. Change repository owner and rights

    You need to change the owner and add some group rights on the repository files, or else you’ll get an error when commiting changes tot the repository over http.

    $ sudo chown www-data:www-data  /var/svn/repos -R
    $ sudo chmod g+w /var/svn/repos -R

    Finally you can test if you can connect to your repository via the url http://127.0.0.1/svn

    4. Install Netbeans 6.8

    Download the installer file Netbeans

    $ chmod +x <installer-file-name>.sh
    $ sudo ./<installer-file-name>.sh

    Accept the defaults. Netbeans will be installed in the /usr/local directory.

    5. Install Glassfish 2.1.1

    Download the installer file Glassfish v2 into a temporary directory

    $ java -Xmx256m -jar <installer-file-name>.jar
    $ mv glassfish ~/opt/glassfishv2
    $ cd ~/opt/glassfishv2
    $ chmod -R +x lib/ant/bin
    $ ./lib/ant/bin/ant -f setup.xml

    Note: To prevent future problems when the jdk version get updated make sure, that glassfish uses the symbolic link to the jdk installation.
    The JDK home is set in the file <glassfish_home>/config/asenv.conf. It should read:

    AS_JAVA="/usr/lib/jvm/java-6-sun/jre/.."
    

    6. Install Jboss-seam

    Download the latest seam distibution from the Seam website and unzip it into ~/opt/

    7. Install Ant

    $sudo apt-get install ant
    

    If you’ve come this far, you”ll have all the components you need to get started with seam-gen!!

    %d bloggers like this: