Help

I am leading the CDI 1.1 specification, and work on JBoss Developer Framework, a set of tutorials and examples for all JBoss users. Previously, I've worked on Infinispan and I led the Seam and Weld projects, and am a founder of the Arquillian project. I've worked on a number of specifications including JSF 2.0, AtInject and Java EE 7. I am a regular speaker at JUGs and conferences such as JavaOne, Devoxx, JAX, JavaBlend, JSFDays, JBoss World, Red Hat Developer Day and JUDCon.

I am currently employed by Red Hat Inc. working on JBoss open source projects. Before working for Red Hat, I used and contributed to Seam whilst working at a UK based staffing agency as IT Development Manager.

Location: Edinburgh, Scotland
Occupation: Principal Software Engineer, Red Hat, Inc.
Archive
24. Sep 2007, 18:41 CET, by Pete Muir

Those of you who keep up with Seam CVS will have noticed a lot of changes recently to the build system. I'm going to give a brief tour of them here, and discuss how they affect you when you use Seam.

Maven

We chose to use Maven to manage Seam dependencies as it offers reasonable support for transitive dependencies, it's emerging as the de-facto standard both in the Java Enterprise world and at JBoss.org, tools like Ant (using Maven Ant Tasks) and Ivy can consume Maven POMs and the 40 odd votes for publishing Seam to Maven pushed us somewhat ;)

Using Seam with Maven

For those Maven pro's among you, here's the essential information to get you started. If you are considering dabbling with Maven, then you might want to wait for a couple of weeks for Seam 2.0.0.GA which will include a brief tutorial on using Maven with Seam.

To checkout the support you'll need to add http://snapshots.jboss.org/maven2 as a snapshot repository to your POM; having done this, you can add Seam to your projects POM:

<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ui</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-pdf</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-remoting</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ioc</artifactId>
</dependency>
<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ioc</artifactId>
</dependency>

We'll shortly start publishing snapshots from our continuous integration server.

As Seam is an integration framework we spend a lot of time ensuring that the integrated libraries work well, both together and with Seam. If you want to always use the recommended library versions with Seam you may want to use the Seam root POM (which just declares repositories and dependencyManagement) as your parent POM:

<parent>
  <groupId>org.jboss.seam</groupId>
  <artifactId>root</artifactId>
  <version>2.0.0.SNAPSHOT</version>
</parent>

You can then just declare your dependencies versionless in your POM:

<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam</artifactId>
</dependency>

<dependency>
  <groupId>org.jboss.seam</groupId>
  <artifactId>jboss-seam-ui</artifactId>
</dependency>

<dependency>
  <groupId>org.jbpm</groupId>
  <artifactId>jbpm-jpdl</artifactId>
</dependency>

You'll then get the SNAPSHOT of Seam core and ui and jBPM jPDL 3.2.1.

Maven/Dependency Management Roadmap

So, what else is on the cards?

  • Write some documentation!
  • Release the next version of Seam to http://repository.jboss.org
  • Improve the handling of JBoss Embedded in the build scripts
  • Alter the seamdiscs example to use Maven for dependency management
  • Provide an option to use Maven-based dependency management in seam-gen

Feedback and Troubleshooting

Whilst this is a good start there a still some bumps to iron out! I'm really hoping for strong (and fast!) feedback on this one to get it just right for the GA.

I've got a couple of questions/discussion points for you maven experts out there:

  • Is there a way to read external properties into maven? As we're using Maven Ant Tasks we can't use -Dproperty=foo and we need to be able to specify the property file relative to the POM's location.
  • Is there a nice way to provide dependency profiles without putting empty jars all over the place?
  • Is using the Seam root POM as your parent POM an acceptable alternative?

If you are following Seam CVS and are having problems building Seam:

  1. Grab the latest CVS
  2. Make sure your build.properties file /doesn't/ contain any references to version or patchlevel
  3. Run ant clean build
  4. If the build fails due to
    The <urn:maven-artifact-ant:dependencies> type doesn't support the "versionsid" attribute.
    you probably have an older version of maven-ant-tasks.jar on your classpath. Try running ant -nouserlib or update Maven Ant Tasks to 2.0.7.
  5. Report your problem on the forum!

Maven in Seam

So, how did we use Seam and Maven Ant Tasks to build Seam?

Building Seam using Maven

However good Maven is at dependency management, we're not in love with using Maven for building software (it seems slow, and incredibly restrictive) so we decided on the half-way house of using Maven Ant Tasks - which are essentially a set of ant tasks that call into Maven. With this, we can use Ant to load dependencies from Maven POM files and publish artifacts to Maven.

In CVS the Seam build uses Maven POM files to declare dependencies. These pom files are stored in /build and, using some Ant substitution filters, are copied to /classes/poms. From these poms, we can build path's (to compile Seam), fileset's (using the Maven Ant Tasks) and can use the handy version mapper (which allows you to copy a dependency fileset stripping off the version information e.g. org/jboss/seam/jboss-el/2.0.0.BETA1/jboss-el-2.0.0.BETA1.jar is copied to /jboss-el.jar).

There is more information in /build/readme.txt

Examples and Seam-gen

For now we've stuck with using ant for building and declaring the dependencies in the build.xml. To do this we copy Seam and it's dependencies (resolved using Maven) to a staging area, and then building the deployable artifacts against this directory. This has the advantage of not having to write POM's for every example (non-trivial given the lack of dependency profiles in Maven).

08. Sep 2007, 03:21 CET, by Pete Muir

Seam has great i8ln - it provides a built in locale selector which it uses to provide localized message bundles. You can load different message bundles for different pages.

Seam's built in message bundle uses properties files to define the resources - but what if you want to store your messages in a database? You might want to allow an admin to edit the properties through a web admin panel for example. Here we will cover the basic entities and wiring needed - you will probably want to add in some CRUD views for the resources.

Entities

We create two entites, first a resource bundle entity, which defines the bundle name, and the locale which it is for:

@Entity
public class ResourceBundle {
        
   @Id @GeneratedValue
   private Integer id;

   private String name;

   private ResourceLocale resourceLocale;
        
   @OneToMany(mappedBy="resourceBundle")
   ¨¨private List<Resource> resources;
}

And the embedded locale:

@Embeddable
public class ResourceLocale {
        
        @Column(length=2)
        @Length(max=2)
        private String language;
        
        @Column(length=2)
        @Length(max=2)
        private String country;
        
        @Column(length=2)
        @Length(max=2)
        private String variant;

}

And the resource itself, consisting of a key and a value:

@Entity
@NamedQueries({
   @NamedQuery(
      name="keys", 
      query="select r.key from Resource r where 
         r.resourceBundle.name = :bundleName and
         r.resourceBundle.resourceLocale.language = :language and 
         r.resourceBundle.resourceLocale.country = :country and
         r.resourceBundle.resourceLocale.variant = :variant"),
   @NamedQuery(
      name="value", 
      query="select r.value from Resource r where 
         r.resourceBundle.name = :bundleName and 
         r.resourceBundle.resourceLocale.language = :language and 
         r.resourceBundle.resourceLocale.country = :country and 
         r.resourceBundle.resourceLocale.variant = :variant and r.key = :key")
})
public class Resource 
{
   @Id @GeneratedValue
   private Integer id;

   @Column(name="_key")
   private String key;

   @Column(name="_value")
   private String value;
   
   @ManyToOne
   private ResourceBundle resourceBundle;
}

Wiring

We need to make Seam use our new resource loading scheme:

@Name("org.jboss.seam.core.resourceLoader")
@BypassInterceptors
public class DatabaseResourceLoader extends ResourceLoader {
   
   // Redefine how we load a resource bundle
   public ResourceBundle loadBundle(final String bundleName) {
      return new ResourceBundle() {

         public Enumeration<String> getKeys() {
            Locale locale = org.jboss.seam.core.Locale.instance();
            EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
            List resources = entityManager.createNamedQuery("keys")
               .setParameter("bundleName", bundleName)
               .setParameter("language", locale.getLanguage())
               .setParameter("country", locale.getCountry())
               .setParameter("variant", locale.getVariant())
               .getResultList();
            return Collections.enumeration(resources);
         }

         protected Object handleGetObject(String key) {
            Locale locale = org.jboss.seam.core.Locale.instance();
            EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");
            try {
               return entityManager.createNamedQuery("value")
                  .setParameter("bundleName", bundleName)
                  .setParameter("language", locale.getLanguage())
                  .setParameter("country", locale.getCountry())
                  .setParameter("variant", locale.getVariant())
                  .setParameter("key", key)
                  .getSingleResult();
            } catch (NoResultException e) {
               return null;
            }
         }
      };
   }
}

Java 6

This approach will work on Java 5 - but I believe Java 6 provides enhanced ResourceBundle support (but as there is no Java 6 for the Mac I didn't ;-) ). You might want to investigate this.

Caching

We've not used any caching here - every request for a key will result in a database lookup. You could use a Java Map to cache resources as they are loaded (taking care to flush the cash when the locale changes - use Seam's event bus to observe org.jboss.seam.localeSelected), or perhaps you could rely on the Hibernate second level cache... Up to you!

Showing 116 to 117 of 117 blog entries