Devoxx 2010 impressions – day 1

Last week I enjoyed 2 great University days at Devoxx. In this blog post i’ll comment on the 2 main talks i’ve visited on monday, i.e. Seam 3 and Spring Roo. Both talks had a lot in common.
In the past both were JEE frameworks which tried to fill a gap in traditional JEE development. The Spring framework simplified for one the whole entity bean fiasco of EJB2 by introducing a model layer based on POJOs and was typically a framework used for integration in the model layer of enterprise development. Springs lessons led to the EJB3 spec in JEE5 and the Managed Bean spec in JEE6.
Seam on the other hand filled the gap of integrating the view and the model layer, making it much easier to couple JSF pages to Session Beans (or their Seam counterparts). Its lessons have led to the CDI spec in JEE6 and (i think) to the EJB Lite spec.
Now, while both were considered a framework in the past, they are now in their latest incarnations both full blown stacks for Java Enterprise Development. And in this they have a lot in common. To name a few:

  • Both have their own (eclipse-)based IDE. For Spring it’s called SpringSource Tool Suite (STS), for Seam we have JBoss Developer Studio;
  • Both have solutions for cross layer concerns, like security, logging, SOAP/REST integration, event handling and AJAX support, to name a few;
  • Both have a solution for building the view layer. Spring has Spring MVC and Seam uses JSF, CDI and facelets;
  • And now both have their own RAD solution. In Spring it’s called SpringRoo, in Seam it’s called SeamForge.

The last 2 components of the stacks are (especially SeamForge) relatively new. Both are shell based and can also be used in Eclipse as a plugin (Of course, their own IDE’s have these plugins installed by default).

I think it’s a good thing that these stacks are emerging. In the end they will provide you with basically everything you need for Enterprise development. And in the end hopefully it will prevent all these hybrid solutions where everyone uses their favorite framework on the job, which hinders maintainability in the long run imho. The SpringSource stack and the JBoss stack are both big players and i honestly believe that commitment to these stacks by the development community will be an ongoing concern for many years to come.
If I had to pick a favorite among the two. I slightly prefer the JBoss stack, because it adheres much more to the JEE specification.

In the next 2 section i’ll summarize some highlights of the 2 talks.

Seam 3: State of the Union

by Pete Muir and Dan Allen

Seam 3 as apposed to the rather monolithic Seam 2 framework is of a modular design. It consists of a set of modules built on top of the core of Seam. This core consists of the reference implementation of CDI, Weld, which currently is supported for the JEE servers JBoss and Glassfish, and for the Web servers Tomcat and Jetty. It is also supported for a standalone JSE environment. Seam 3 is hosted at GitHub and uses Maven for the build lifecycle. There was also talk about the use of Gradle in the near future.

As i sad before. Seam’s 3 approach is more modular than that of its predecessor. Here’s a list of the modules that were presented during the talk:

  • Seam Solder, aka Weld extensions. This is the core of Seam 3 and Pete and Dan stressed that this is the module everyone should use. It provides to name but a few:
    • A lot of familiar Seam 2 annotations (eg. @Requires, @Unwraps);
    • Annotations for Logging;
    • Annotations for Resource handling;
    • Annotations to be used for queries in DAO’s;
  • SeamConfig, which extends the beans.xml configuration file for Seam related configurations in xml-style;
  • SeamInternational, for i18n an l10n support;
  • SeamFaces, which is an enhancement for JSF 2. It contains, to name a few, the familiar (Seam 2) @Conversation scope, support for multiple field validation, composite components and a simple front controller called ViewAction;
  • SeamPersistence, the module for declarative persistence, transactions, manual flushing and the likes;
  • SeamCath for error and event handling;
  • SeamServlet, which provides Servlet enhancements;
  • SeamRest, for REST integration;
  • SeamGWT, for GWT integration.

And already mentioned a big part of the presentation was reserved for a demonstration of the current state of the SeamForge project. It’s the successor to seam-gen. You can use it for RAD. It can generate an entire CRUD application out of the box based on a couple of simple database tables or defined entities. It is shell based and also comes as an IDE (Eclipse) plugin.  I was really impressed by the demo. As with seam-gen, you don’t have to mess with all the configuration files anymore, because you can let SeamForge do this for you. In no time you can generate a fully working demo application for your customers or one to be used as a reference for yourself for development. And SeamForge does this all in Maven style, which is big plus compared to the ANT style seam-gen, if i may say so. I can’t wait to try it all out for myself.

Another big topic was the Arquillian project. It contains for one an embedded container used for testing an enterprise application. Arquillian can be integrated in a testing framework like JUNIT or TestNG. Control of the container is handled via ANT tasks or a Maven plugin.

Extreme Productivity with Spring Roo

by Ben Alex and Stefan Schmidt

Spring Roo is a project within the SpringSource stack comparable to SeamForge. You can generate a CRUD application with it in no time in the Spring style. Like SeamForge, SpringRoo generates a Maven application, it can be controlled via highly intelligent shell commands and also comes with an Eclipse plugin.

It makes heavy use of AspectJ files. For example it puts the getter and setter methods of the generated JPA classes in a seperate AspectJ file per class. This keeps the JPA classes very clean. Another benefit is that the AspectJ classes can be generated (or even incrementally updated) so they can be omitted from the version control system. If you have the AspectJ plugin installed in Eclipse, code completion will work even for the code in the AspectJ files like those getter and setter methods. Of course STS comes with this plugin already installed.

The JPA classes that are generated by SpringRoo contain JPA 2 annotations and a couple of SpringRoo specific annotations.

Removing all of SpringRoo from your application can be done in three simple steps:

  1. Refactor the AspectJ code back in your Java code (can be done via the AspectJ plugin);
  2. Remove The SpringRoo annotations (search/delete);
  3. Remove the SpringRoo jar from you pom.xml.

Another part of the presentation showed the use of the Hades plugin for SpringRoo. With Hades you can generate a generic DAO layer on top of your JPA layer. Very powerful stuff.

The last big part of the presentation showed the view layer generation capabilities of SpringRoo. SpringRoo lets you generate a Spring MVC layer and takes care of a lot of scaffolding for you, again removing you of the burden of getting all those configuration files right. It eg. can take care of:

  • REST support
  • Templating support
  • Themes
  • i18n, l10n
  • Bean validation
  • Security
  • Spring WebFlow

All in all, this was the second time this day i was pretty impressed, and again I can’t wait to play with this stuff.

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.

Maven resource filtering not working

I recently ran into a problem when using maven resource filtering from within Eclipse. The problem was that properties in xml files (in this case a spring configuration file) weren’t expanded. Eventually i figured out what was causing the problem. That’s what this small blog is about.

Setup

As noted i wanted maven to expand some properties – datasource properties in this case – in a spring configuration file. The idea was to externalize the datasource connection information in various environment files, so that during deployment the right datasource connection information would be put in the spring configuration file according to the deployment environment, eg. local, test, staging.

For this i altered the spring conguration file, called spring-config.xml in my case, and changed the configuration for the dataSource spring bean into the one resembling the xml stanza below:

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName">
    <value>oracle.jdbc.driver.OracleDriver</value>
  </property>
  <property name="url">
    <value>jdbc:oracle:thin:@${db.host}:${db.port}:${db.sid}</value>
  </property>
  <property name="username">
    <value>${db.user}</value>
  </property>
  <property name="password">
    <value>${db.password}</value>
  </property>
</bean>

This file was stored in the default resource directory src/main/resources.

The property files were put in the src/main/filters directory and were called filter-<env>.properties, eg. the filter-local.properties file looked like this:

#spring-config.xml
db.host=localhost
db.port=1521
db.sid=XE
db.user=scott
db.password=tiger

In the pom file i added the following code necessary for the property expanding:

<build>
  <filters>
    <filter>src/main/filters/filter_${env}.properties</filter>
  </filters>
  <resources>
    <resource>
      <directory>src/main/resources</directory>
      <filtering>true</filtering>
    </resource>
  </resources>
</build>
....
<profiles>
  <profile>
    <id>env-local</id>
    <properties>
      <env>local</env>
    </properties>
  </profile>
  <profile>
    <id>env-test</id>
    <properties>
      <env>test</env>
    </properties>
  </profile>
....
</profiles>

Now i used eclipse with the m2eclipse plugin and defined the following configuration for packaging the project (a web application) to a war file suitable for my local environment:

Notice the embedded Maven Runtime which is selected by default.

Problem

After running the configuration the properties for the datasource bean were not expanded and the original spring configuration file for the local deployment got packaged. I also noticed that maven properties in property text files as opposed to xml files, did get expanded. So the problem only occurred in xml files.

Solution

I noticed that running the package maven goal from a terminal window did expand the properties. So eventually i realized that the embedded maven version selected by default was the cause of distress. After i switched the maven runtime for the run configuration to the maven version currently installed on my ubuntu distribution, the problem was solved. See the picture below for this configuration.

The maven version on my ubuntu distribution, by the way, was installed via the following command:

sudo apt-get install maven2

Note that in future versions the bug in the maven embedded runtime may be fixed.

Sortable and clickable lists with Apache Wicket

For my recent project I had to implement a sortable and clickable list. The project uses Apache Wicket as the front-end. It took me a while to get it all working and after some refactoring I was able to produce a generic solution to this problem. This solution is based on the following assumptions

  • The list to be sorted is based on a JPA entity and all the columns that need to be sortable are available as simple getter methods on the entity;
  • The entity has an id attribute, which uniquely identifies it;
  • The equals() and hasCode() methods of the entity will be overriden. Two entitiy instances will  be considered equal if their ids match;
  • The JavaScript jQuery library is available in the Wicket page displaying the list.

See the image below for an example of the eventual sortable and clickable list on which this blog is based.

The list shows the Account entity. All the columns are sortable and clicking on a row will open the url on which the hyperlink of the first column is based. There’s also a pagination link available in case the number of rows exceeds the number of rows displayed. In the remainder of this blog I’ll walk through all the necessary components.

Model

Interface ISortableEntity

For an entity to be able to be sorted in a list, I’ve defined the following interface:


package com.example.entity;

import java.math.BigDecimal;

public interface ISortableEntity {

 public BigDecimal getId();
 public void setId(BigDecimal id);
 public ISortableEntity newSearchInstance();

}

As you can see, the entity has to have an id attribute. It also has to implement the newSearchInstance() method which basically has to return a new instance of the entity class.

Entity Account

The list is based on an entity called com.example.entity.Account. The entity has to implement the ISortableEntity interface and has to override the equals() and hashCode() methods. As mentioned before two entities are considered equal if their ids match.

package com.example.entity;

import java.math.BigDecimal;
import javax.persistence.*;

@Entity
@Table(name = "ACCOUNTS")
@SequenceGenerator(allocationSize = 1, name = "account_id_generator", sequenceName = "accounts_id_seq")
public class Account implements java.io.Serializable, ISortableEntity {

	private static final long serialVersionUID = -1213397204916173215L;
	private BigDecimal id;
	private BigDecimal accountNumber;
	private String firstName;
	private String lastName;
	private String username;

	public Account() {
	}

	public Account(BigDecimal id) {
		this.id = id;
	}

	public Account(BigDecimal id, BigDecimal accountNumber, String firstName,
			String lastName, String username) {
		this.id = id;
		this.accountNumber = accountNumber;
		this.firstName = firstName;
		this.lastName = lastName;
		this.username = username;
	}

	@Id
	@GeneratedValue(generator = "account_id_generator")
	@Column(name = "ID", unique = true, nullable = false, precision = 22, scale = 0)
	public BigDecimal getId() {
		return this.id;
	}

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

	@Column(name = "ACCOUNT_NUMBER", precision = 22, scale = 0)
	public BigDecimal getAccountNumber() {
		return this.accountNumber;
	}

	public void setAccountNumber(BigDecimal accountNumber) {
		this.accountNumber = accountNumber;
	}

	@Column(name = "FIRST_NAME", length = 100)
	public String getFirstName() {
		return this.firstName;
	}

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

	@Column(name = "LAST_NAME", length = 100)
	public String getLastName() {
		return this.lastName;
	}

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

	@Column(name = "USERNAME", length = 50)
	public String getUsername() {
		return this.username;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Account other = (Account) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

	public ISortableEntity newSearchInstance() {
		ISortableEntity searchAccount = new Account();
		return searchAccount;
	}
}

View

For the view layer the sortable list of accounts will be displayed on a Wicket Panel. For this we’ll need a template and the corresponding Java class. For the list to be sortable we first have to extend the abstract class org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider. For one of its methods, we also need to extend the abstract class org.apache.wicket.model.LoadableDetachableModel. I’ve generified both of these subclasses so they only depend on the aforementioned interface ISortableEntity. The code for these classes is shown below.

SortableEntityProvider

package com.example.view.common;

import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.wicket.extensions.markup.html.repeater.util.SortParam;
import org.apache.wicket.extensions.markup.html.repeater.util.SortableDataProvider;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.PropertyModel;

import com.example.entity.ISortableEntity;

public class SortableEntityProvider<T extends ISortableEntity> extends
		SortableDataProvider<T> {
	private static final long serialVersionUID = -1646363525627999438L;
	private List<T> data;

	protected List<T> getData() {
		return data;
	}

	protected void setData(List<T> data) {
		this.data = data;
	}

	public SortableEntityProvider(List<T> data, String defaultSort) {
		super();
		setData(data);
		// set default sort
		setSort(defaultSort, true);
	}

	public Iterator<? extends T> iterator(int first, int count) {
		final SortParam sp = getSort();

		Collections.sort(getData(), getComparator(sp));

		return getData().subList(first, first + count).iterator();
	}

	public int size() {
		return getData().size();
	}

	public IModel<T> model(T object) {
		return new DetachableEntityModel<T>(object, getData());
	}

	protected Comparator<T> getComparator(final SortParam sp) {
		return new Comparator<T>() {
			public int compare(T arg0, T arg1) {

				int result;

				PropertyModel<Comparable> model0 = new PropertyModel<Comparable>(
						arg0, sp.getProperty());
				PropertyModel<Comparable> model1 = new PropertyModel<Comparable>(
						arg1, sp.getProperty());

				if (model0.getObject() == null && model1.getObject() == null) {
					result = 0;
				} else if (model0.getObject() == null) {
					result = -1;
				} else if (model1.getObject() == null) {
					result = 1;
				} else {
					result = model0.getObject().compareTo(model1.getObject());
				}

				if (!getSort().isAscending()) {
					result = -result;
				}

				return result;

			}
		};
	}
}

The constructor takes a list with all the rows displayed on the panel (usually a list of entities returned by a service calling a DAO) as the data parameter, along with a string representation of the attribute providing the default sort, eg. “firstName” for default sorting on the First Name of the Account.

DetachableEntityModel

package com.example.view.common;

import java.math.BigDecimal;
import java.util.List;

import org.apache.wicket.model.LoadableDetachableModel;

import com.example.entity.ISortableEntity;

public class DetachableEntityModel<T extends ISortableEntity> extends LoadableDetachableModel<T> {
	private static final long serialVersionUID = -2133620119433783150L;

	private final ISortableEntity entity;
	private final List<T> data;

	public DetachableEntityModel(ISortableEntity a, List<T> data) {
		this.entity = a;
		this.data = data;
	}

	public int hashCode() {
		return getId().hashCode();
	}

	public BigDecimal getId() {
		return entity.getId();
	}

	public boolean equals(final Object obj) {
		if (obj == this) {
			return true;
		} else if (obj == null) {
			return false;
		} else if (obj instanceof DetachableEntityModel) {
			DetachableEntityModel other = (DetachableEntityModel) obj;
			return other.getId() == getId();
		}
		return false;
	}

	protected T load() {
		ISortableEntity searchEntity = entity.newSearchInstance();
		searchEntity.setId(getId());
		return data.get(data.indexOf(searchEntity));
	}
}

AccountsPanel.html

<html>
<body>
<wicket:panel>
  <h2>Accounts</h2>
  <table class="clickable">
    <thead>
      <th wicket:id="orderByAccountNumber">[Account number]</th>
      <th wicket:id="orderByFirstName">[First name]</th>
      <th wicket:id="orderByLastName">[Last name]</th>
      <th wicket:id="orderByUsername">[Username]</th>
    </thead>
    <tbody>
      <tr wicket:id="accountRow">
        <td><a wicket:id="id" href="#"><span wicket:id="idSpan">[id]</span></a></td>
        <td wicket:id="firstName">[First name]</td>
        <td wicket:id="lastName">[Last name]</td>
        <td wicket:id="username">[Username]</td>
      </tr>
    </tbody>
  </table>
  <br />
  <span wicket:id="navigator">[dataview navigator]</span>
</wicket:panel>
</body>
</html>

The css class clickable will take care of the styling of the table and is coupled to the jQuery code (shown later on) that will implement the row clicks. A row click will make sure that the hyperlink coupled to the first column in the table will be clicked. The page also contains a component that will take care of the pagination.

AccountsPanel.java

package com.example.view.common;
....
public class AccountsPanel extends Panel {
 private static final long serialVersionUID = -395786600454932830L;

 @SpringBean
 AccountService accountService;

 public AccountsPanel(String id) {
 super(id);

 drawChildren();
 }

 private void drawChildren() {

 List<Account> accounts = accountService.getAccounts();

 SortableDataProvider<Account> dp = new SortableEntityProvider<Account>(
 accounts, "lastName");

 final DataView<Account> dataView = new DataView<Account>(
 "accountRow", dp) {
 private static final long serialVersionUID = 6364718764586838158L;

 @Override
 public void populateItem(final Item<Account> item) {
 item.add(new SimpleAttributeModifier("class",
 item.getIndex() % 2 == 0 ? "even" : "odd"));

 Link<Account> idLink = new Link<Account>("id") {
 private static final long serialVersionUID = -480222850475280108L;

 @Override
 public void onClick() {
 //TODO: Do stuff
 }
 };
 idLink.add(new Label("idSpan", item.getModelObject().getId()
 .toPlainString()));
 item.add(idLink);

 item.add(new Label("firstName", item.getModelObject()
 .getFirstName()));
 item.add(new Label("lastName", item.getModelObject()
 .getLastName()));
 item.add(new Label("username", item.getModelObject()
 .getUsername()));
 }
 };

 dataView.setItemsPerPage(5);

 addSorting(dp, dataView);

 add(dataView);

 add(new PagingNavigator("navigator", dataView));
 }

 private void addSorting(SortableDataProvider<Account> dp,
 final DataView<Account> dataView) {
 add(new SortableOrderBy<Account>("orderByAccountNumber", "id", dp,
 dataView));
 add(new SortableOrderBy<Account>("orderByLastName", "lastName", dp,
 dataView));
 add(new SortableOrderBy<Account>("orderByFirstName", "firstName", dp,
 dataView));
 add(new SortableOrderBy<Account>("orderByUsername", "username", dp,
 dataView));
 }

}
  • lines 6,7,17: Service (in this case a Spring Bean) that will cough up the list of accounts for display;
  • line 19,20: Here the SortableDataProvider is initialized with the accounts list. The accounts will be sorted on “lastName” by default;
  • line 22,23: The SortableDataProvider is fed to a DataView coupled to the accountRow table row wicket component;
  • line 36: This will be the code executed when the row is clicked;
  • line 52: The list displayed will contain 5 accounts;
  • line 54: Here the table row headers are added. They will be clickable for sorting the corresponding table column they belong to. I’ve created a small subclass for OrderByBorder, called SortableOrderBy so I don’t have to repeat the code that resets the pagination upon sort (the code will be provided below). Note that the second argument of the SortableOrderBy constructor is couple to the getter method on the Account entity. You can also apply sorting to properties of a related entity. Let’s presume that the Account entity has a relationship with an Address entity. If you would like to display say the city of the Address in the Accounts list and make it sortable, the second argument would be address.city ;
  • line 58: Here the paginator for the list is added to the panel.

SortableOrderBy.java

package com.example.view.common;

import org.apache.wicket.extensions.markup.html.repeater.data.sort.ISortStateLocator;
import org.apache.wicket.extensions.markup.html.repeater.data.sort.OrderByBorder;
import org.apache.wicket.markup.repeater.data.DataView;

public class SortableOrderBy<T> extends OrderByBorder {

  private static final long serialVersionUID = 5017011967951860645L;
  private final DataView<T> dataView;

  public SortableOrderBy(String id, String property,
      ISortStateLocator stateLocator, DataView<T> dataView) {
    super(id, property, stateLocator);
    this.dataView = dataView;
  }

  @Override
  protected void onSortChanged() {
    dataView.setCurrentPage(0);
  }
}

As I said, this is just a simple subclass to reset the current page to the first one after the sorting has been applied.

css

.clickable tr.even {
    background-color:#E9F9FF;
}

.clickable tr:hover {
    background-color: #F0F0F0;
}

.clickable td:hover {
    cursor: pointer;
}

I’m just showing the most important style elements. The above will make sure that:

  • the odd and even rows of the table are of different color;
  • the row that you hover over will change its color;
  • the arrow cursor will be replaced by a pointer cursor when you hover over a clickable row;

jQuery

The last piece of the puzzle is a small piece of jQuery code that will make sure the correct styling is applied and that a row click will lead to the clicking of the corresponding hyperlink programmatically.

$(document).ready(function() {

    $('.clickable tr').click(function() {
        var href = $(this).find("a").attr("href");
        if(href) {
            window.location = href;
        }
    });

})

And that’s all there is to it. With the code shown in this blog you can transform any list of entities into a sortable and clickable one.

Setup a local smtp server for JBoss

In my Seam application I needed a local smtp server for testing the email facilities Seam offers. After some research on the internet I came up with a configuration which uses Ubuntu’s Mail Transfer Agent postfix with Gmail as the so called smarthost. Postfix will act as an intermediate smtp server which will dispatch the mails it receives to Gmail‘s smtp server.
This blog will summarize the steps needed for this setup.

Postfix installation and configuration

First, let’s install postfix.

sudo apt-get install postfix

Next, add additional configuration.

sudo dpkg-reconfigure postfix

Choose the following settings. For settings not listed below, you can keep the default settings.

  • General type of mail configuration: Internet with smarthost
  • SMTP relay host: smtp.gmail.com:587
  • Internet protocols to use: all

Next add settings necessary for authentication against the Gmail smtp server.

sudo postconf -e 'smtp_use_tls = yes'
sudo postconf -e 'smtp_sasl_auth_enable = yes'
sudo postconf -e 'smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd'
sudo postconf -e 'smtp_sasl_security_options = noanonymous'
sudo postconf -e 'smtp_sasl_tls_security_options = noanonymous'

Next add the password file /etc/postfix/sasl_passwd containing the username and password of your Gmail account.

smtp.gmail.com username@gmail.com:PASSWORD

Now, hash the password file (this will create the hashed sasl_passwd.db file), throw away the original file and restart the postfix daemon.

sudo postmap /etc/postfix/sasl_passwd
sudo rm /etc/postfix/sasl_passwd
sudo /etc/init.d/postfix restart

Testing postfix

You can now test the postfix configuration via telnet.

telnet localhost 25
>mail from:noreply@gmail.com
>rcpt to:username@gmail.com
>data
>Testmail
>.
>quit

This should send a small email to username@gmail.com.

Troubleshooting

If the mail didn’t arrive, you can check a couple of things:
Check if it’s in the postfix queue:

sudo mailq

Flush the queue:

sudo postfix -f

Check the log file:

sudo tail /var/log/mail.log

JBoss configuration

Next we’re gonna add the mail server configuration to JBoss, so it van be accessed via JNDI. Edit the file $JBOSS_HOME/server/default/deploy/mail-service.xml.

<?xml version="1.0" encoding="UTF-8"?>
<server>
  <mbean code="org.jboss.mail.MailService"
         name="jboss:service=Mail">
    <attribute name="JNDIName">java:/Mail</attribute>
    <attribute name="Configuration">
      <configuration>
        <!-- Change to your mail server prototocol -->
        <property name="mail.transport.protocol" value="smtp"/>
        <!-- Change to the SMTP gateway server -->
        <property name="mail.smtp.host" value="localhost"/>
        <!-- The mail server port -->
        <property name="mail.smtp.port" value="25"/>
        <!-- Change to the address mail will be from  -->
        <property name="mail.from" value="noreply@gmail.com"/>
        <!-- Enable debugging output from the javamail classes -->
        <property name="mail.debug" value="false"/>
      </configuration>
    </attribute>
    <depends>jboss:service=Naming</depends>
  </mbean>
</server>

Seam configuration

Finally we need to configure seam so it can communicate with our new smtp server. This is done in the component descriptor components.xml.

<?xml version="1.0" encoding="UTF-8"?>
<components
  xmlns="http://jboss.com/products/seam/components"
  xmlns:mail="http://jboss.com/products/seam/mail"
  xsi:schemaLocation="http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.2.xsd
    http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">
  ....
  <mail:mail-session session-jndi-name="java:/Mail" />
  ....
</components>

If you’re also using jBPM for business processes within your seam application and need email facilities, you can configure the smtp server in the jBPM configuration file.

<?xml version="1.0" encoding="UTF-8"?>
<jbpm-configuration>
  ....
  <string name="jbpm.mail.smtp.host" value="localhost"/>
  <string name="jbpm.mail.from.address" value="noreply@open18.org"/>
</jbpm-configuration>

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;
    	}
    	....
    }
    

    Starting a jBPM pageflow

    This week i struggled a bit to get a jBPM pageflow running from my seam-gen application. It’s one of the examples in the Seam in Action book from Manning. The goal is to kick off a wizard containing a couple of screens for entering a new entity (in this case a golf course) in the database. As the wizard adds a new record in the database, one of the requirements in the application is that the user doing this is logged in.

    There are basically 2 ways to accomplish this. You can start the pageflow in the first page of the wizard during its Render Response phase by making use of a Seam factory. Your other option is to kick off the pageflow from an action method. The second method seems the more natural to me and is the one that gave me some headaches. I’ll shortly describe both the options and provide a simple solution to the issues regarding the second option.

    Example

    A golf facility can contain multiple golf courses. To add a golf course to a facility you can launch a course wizard which is linked to a jBPM pageflow.

    Launching the course wizard from the Facility page.

    In this flow we have a facility page /Facility.xhtml which is bound to the Seam component facilityHome and the first page of the course wizard /coursewizard/BasicCourseInfo.xhtml which is bound to the Seam components courseWizard and course (which contains the actual course being added). The pageflow is defined in the jPDL file courseWizard-pageflow.jpdl.xml.

    Adding a course to the database requires a user that’s logged in. This restriction is applied in the general page descriptor pages.xml by the following XML stanza

    <page view-id="/coursewizard/*" login-required="true"/>
    

    Starting the wizard on its first page

    In this option, the Add Course… button on the facility page contains navigation to the first page of the wizard and passes the id of the current facility:

    <s:button id="courseWizard" value="Add course..."
     propagation="none" view="/coursewizard/BasicCourseInfo.xhtml">
    	<f:param name="facilityId" value="#{facilityHome.instance.id}" />
    </s:button>
    

    The input components on the /coursewizard/BasicCourseInfo.xhtml page are all bound to a conversation scoped Seam component named course. This component is created by a factory in the courseWizard component. The facility is weaved in by a request parameter:

    @Name("courseWizard")
    @Scope(ScopeType.CONVERSATION)
    public class CourseWizard implements Serializable {
        @In
        protected EntityManager entityManager;
        @RequestParameter
        protected Long facilityId;
        @Out
        protected Course course;
        ....
    
        @Begin(pageflow = "Course Wizard", flushMode = FlushModeType.MANUAL)
        @Factory("course")
        public void initCourse() {
            course = new Course();
            course.setFacility(entityManager.find(Facility.class, facilityId));
            // setup some defaults
            course.setNumHoles(18);
            course.setFairways("BENT");
            course.setGreens("BENT");
        }
        ....
    }
    

    Finally the pageflow is started in the jPDL file

    <?xml version="1.0" encoding="UTF-8"?>
    <pageflow-definition xmlns="http://jboss.com/products/seam/pageflow"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://jboss.com/products/seam/pageflow http://jboss.com/products/seam/pageflow-2.0.xsd"
    	name="Course Wizard">
    	<start-page name="basicCourseInfo" view-id="/coursewizard/basicCourseInfo.xhtml"
    		no-conversation-view-id="/CourseList.xhtml">
    		<redirect />
    		<transition name="cancel" to="cancel" />
    		<transition name="next" to="description" />
    	</start-page>
    	....
    </pageflow-definition>
    

    And that’s all there is to it. You click on the Add course… button, the login screen appears (if you’re not logged in) and after successfully logging into the application the first page of the course wizard appears.

    Starting the wizard from an action method

    Now for this second option the Add course… button will be coupled to an action method which will launch the pageflow.
    Once again the code behind the button, which now contains the action method but doesn’t contain navigation anymore. The facilityid parameter isn’t needed anymore as it’s weaved in before the wizard is launched.

    <s:button id="courseWizard" value="Add course..."
    	action="#{courseWizard.addCourse}" propagation="none">
    </s:button>
    

    Now the action method will take care of launching the pageflow. It also weaves the facility into the course to be created.

    
    @Name("courseWizard")
    @Scope(ScopeType.CONVERSATION)
    public class CourseWizard implements Serializable {
    
    	@In
    	protected EntityManager entityManager;
    	@RequestParameter
    	protected Long facilityId;
    	@Out
    	protected Course course;
    	....
    
    	@Begin(join = true, pageflow = "Course Wizard", flushMode = FlushModeType.MANUAL)
    	public void addCourse() {
    		course = new Course();
    		course.setFacility(entityManager.find(Facility.class, facilityId));
    		// setup some defaults
    		course.setNumHoles(18);
    		course.setFairways("BENT");
    		course.setGreens("BENT");
    	}
    	....
    }
    

    Because you start the pageflow from an action method, you have to define a start-state element in your jPDL file as opposed to the start-page element in the previous example.

    <?xml version="1.0" encoding="UTF-8"?>
    <pageflow-definition xmlns="http://jboss.com/products/seam/pageflow"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://jboss.com/products/seam/pageflow http://jboss.com/products/seam/pageflow-2.0.xsd"
    	name="Course Wizard">
    	<start-state>
    		<transition to="basicCourseInfo" />
    	</start-state>
    
    	<page name="basicCourseInfo" view-id="/coursewizard/basicCourseInfo.xhtml"
    		no-conversation-view-id="/CourseList.xhtml">
    		<redirect />
    		<transition name="cancel" to="cancel" />
    		<transition name="next" to="description" />
    	</page>
    	....
    </pageflow-definition>
    

    Now, when clicking the Add course… button when you’re not logged in yet, the login screen will NOT be displayed. Instead you’ll get the following error when using firefox

    The page isn't redirecting properly
    Firefox has detected that the server is redirecting the request for
    this address in a way that will never complete.
    

    Redirect error

    If  firebug is enabled, you can see that the system gets into some sort of redirect loop. I tried a lot of things to fix this issue and eventually stumbled upon a solution which is really very simple. You just have to make sure that the user is logged in before the pageflow gets called. This is done by securing the action method which calls the pageflow. Just add a Restrict annotation.

    	@Begin(join = true, pageflow = "Course Wizard", flushMode = FlushModeType.MANUAL)
    	@Restrict("#{identity.loggedIn}")
    	public void addCourse() {
    		course = new Course();
    		course.setFacility(entityManager.find(Facility.class, facilityId));
    		// setup some defaults
    		course.setNumHoles(18);
    		course.setFairways("BENT");
    		course.setGreens("BENT");
    	}
    

    And that’s it. Now the solution with the action method will behave exactly the same as the solution without it. You click the Add course… button, the login screen appears and after successfully logging in, the first page of the course wizard appears.

    %d bloggers like this: