Chapter 5. The Seam component descriptor

This chapter covers

  • Creating component definitions in XML
  • Defining component XML namespaces
  • Configuring a component’s properties
  • Using the Seam resource bundle

Seam embraces annotations to keep you out of the XML weeds. You are a Java (or Groovy) developer, darn it, and that’s the language in which you should be allowed to program your application! Despite this pragmatic statement, it would be misleading to say that Seam eliminates XML entirely. It doesn’t. If you’re one of those XML enthusiasts, you’ll be glad to know that you don’t have to give up your angled brackets when you move to Seam. In fact, there are some areas of Seam where XML configuration is the best choice—or even the only choice. One example is Seam’s page descriptor, covered in chapter 3, which administers Seam’s page-oriented functionality.[1] Since views are defined in XML, it’s only natural for page controls to be defined that way as well. The use of XML also ensures a quick turnaround by avoiding the compilation step, which is important given that views often require a lot of tinkering.

1 Java-based page configuration is in the pipeline, so this requirement may not hold true for long.

Component definitions are another example where XML proves useful. Annotations such as @Name are easy enough to add to classes under your control. However, if the class you intend to declare as a Seam component is sealed in a third-party JAR file or maintained by another team, annotations don’t do you much good. There may also be situations where you need to alter an existing component definition—either one from your application or one of Seam’s built-in components—or assign property values to that component. In these cases, you have to resort to external configuration, which this chapter covers.

In the previous chapter, you gained an appreciation for the simplicity that annotations bring to the development of Seam components. If you are content working with annotations, I encourage you to skip ahead to the next chapter to learn about another set of annotations that are used to wire components together and to initialize context variables. On the other hand, if you are interested in learning how to define and configure components in XML, this chapter shows how this task is accomplished using the Seam component descriptor. The component descriptor contains XML-based metadata that offers a way to keep the component definition separate from the class. You can also use this file to assign initial property values to component instances, wire components together, override the settings of existing components, and control built-in Seam functionality. The XML in Seam isn’t all old-fashioned, though. Thanks to XML namespaces, you may almost mistake the XML for a real language. In addition to XML, you’ll learn that Java properties files can be used to accomplish certain types of configuration in Seam. We’ll look at Seam’s internationalization (i18n) support as an example of configuring a built-in Seam component and how keys in a message bundle can in turn be used to supply locale-specific values to the properties of a Seam component. By the end of the chapter, you’ll appreciate that both XML and Java properties serve as a valuable supplement to Seam’s primarily annotation-based approach.

5.1. Defining components using XML

The last thing the world needs is another XML configuration file, right? After the debacle that was EJB 2, a major theme of the EJB 3 rework was to do away with required XML descriptors. That theme carries into Seam. As promised in the previous chapter, Seam components can be authored strictly using annotations. So, while Seam has an XML-based component descriptor, its use is entirely optional. Seam happily bootstraps in its absence.

Let’s talk about those cases, though, when annotations are not well suited and XML is warranted. The component descriptor supplements annotations in the following cases:

  • Declare a class, which you can’t modify and that lacks a @Name annotation, as a Seam component (admittedly you could extend the class as an alternative)
  • Install a class that’s not installed by default (the class has an @Install annotation indicating that the component shouldn’t be installed)
  • Override a component definition setting, such as the scope or autocreate value (the value from the descriptor always takes precedence over the annotation)
  • Configure bean properties of components that get applied to the component instance (perhaps to externalize deployment-specific information)

There’s also the possibility that you simply prefer XML over annotations. In that case, you can use the component descriptor to define all of your Seam components. Seam affords you that flexibility, though I don’t recommend that approach. Either way, Seam has a wealth of built-in functionality that is only an XML element away. Likewise, you can use the component descriptor to dress up your own components once they are set in stone (i.e., compiled).

I begin by providing an overview of the component descriptor; what it is, where it lives, and the syntax it uses. I then explain how to use it to define and configure components.

5.1.1. Choosing your descriptor strategy

Seam’s XML-based component configuration can be partitioned across many files. The component descriptor is a general term for the combined sum of the configurations in all of the component descriptors on the classpath. Note that descriptor is a fancy term for XML file.

Seam supports both general descriptors and fine-grained descriptors. The general descriptor can hold an arbitrary number of component definitions, whereas the fine-grained descriptor is designed to govern the components for a single class. The name of the general descriptor is components.xml, whereas fine-grained descriptors are named using the file extension .component.xml.

The general descriptor is often placed in the WEB-INF directory of the web application, which is where seam-gen stashes it. However, this file need not be confined to the WEB-INF directory. Instead, it can be distributed across the classpath, allowing you to organize your configuration the way that’s most suitable for you, rather than having to jam every last component definition into a single file. One recommendation is to partition your component descriptors by module so that each descriptor is centered on the classes within that artifact. You can narrow it even further by putting a general descriptor in each Java package. The most extreme solution to avoiding monolithic component descriptors is to define every component in a fine-grained descriptor adjacent to the subject class. The choice is up to you.

The rules regarding where the component descriptors can be placed are fairly loose. The locations that the Seam component scanner visits are summarized in table 5.1, listed in the order that they’re addressed by the scanner. Regardless of how you decide to divide up your XML-based component configuration, Seam collects them, combines them with the settings defined in annotations, and assembles a unified set of component definitions in memory. From that unified set is where instances are born, as you learned in the previous chapter.

Table 5.1. The resource locations where Seam looks for XML-based component descriptors

Resource

Location details

WEB-INF/components.xml Located in a web application archive (WAR)
META-INF/components.xml Located in any classpath entry (root of JAR or WEB-INF/classes in WAR)
components.xml or *.component.xml Located anywhere in a scanned classpath entry (a classpath entry is scanned if it has a marker file, as described in section 4.5.1 of chapter 4)

 

Tip

Although seam-gen places the components.xml file in the WEB-INF directory, consider storing it in the META-INF directory instead, where it’s accessible to unit and integration test environments that don’t recognize the WEB-INF directory as part of the classpath.

 

That sums up where the component descriptor can be placed. Let’s open up the file, have a look at its structure, and learn how to use it to create component definitions.

5.1.2. The structure of the component descriptor

The Seam component descriptor consists of one or more component definitions, declared using the <component> element and nested within the root <components> element. (In a fine-grained component descriptor, <component> can be the root element.) In addition to generic <component> elements, Seam supports extension elements through the use of XML namespaces to accommodate “type-safe” XML component declarations. The component descriptor also accommodates a handful of noncomponent elements, such as <import> and <factory>, that are covered later in this chapter and the next.

 

Note

If you’ve worked with the Spring configuration file, you should feel right at home using the Seam component descriptor. The main difference is that instead of having a root <beans> element and child <bean> elements, the Seam component descriptor has a root <components> element and child <component> elements. Both Seam and Spring support extension elements using XML namespaces.

 

Listing 5.1 shows a simple component descriptor with two components defined. For the components shown, it is assumed that the @Name and @Scope annotations are absent from the class definitions. Instead, these classes are declared to be components using XML. If the classes had @Name annotations equivalent to these definitions, an exception would result for reasons that are described in section 5.4 (which covers component definitions overrides).

Listing 5.1. A component descriptor with two component definitions
  <components xmlns="http://jboss.com/products/seam/components"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
    http://jboss.com/products/seam/components
    http://jboss.com/products/seam/components-2.0.xsd">

   <component name="newGolfer"
    class="org.open18.model.Golfer" scope="event"/>
   <component name="passwordBean"
    class="org.open18.auth.PasswordBean" scope="event" auto-create="true"/>

  </components>

If Spring hasn’t made you tired of XML yet, then these definitions don’t look so bad. The <component> element defines a new Seam component for the class specified in the class attribute. The name attribute is equivalent to the @Name annotation and the scope attribute is equivalent to the @Scope annotation. The mappings between component definition annotations and the XML <component> element attributes are shown in table 5.2.

Table 5.2. The correlation between Seam annotations and the component descriptor

Class-level annotation or annotation attribute

XML attribute on <component>

Java class name class
@Name name
@Scope scope
@AutoCreate auto-create
@Install(value) installed
@Install(precedence) precedence
@Startup startup
@Startup(depends) startupDepends (Seam 2.1 or greater)
@JndiName[a] jndi-name[a]

a The @JndiName annotation and jndi-name attribute are only relevant for EJB session bean components.

You may notice that the XML equivalent of the @Role annotation is missing from this list. Actually, it’s not. It’s the <component> element itself. The component descriptor supports an arbitrary number of component definitions for a single class. The only requirement is that you assign a different component name to each definition using the name attribute. In effect, the name attribute is the role name. You may find the <component> declaration to be more suitable for defining roles than the @Role annotation, as I have.

Here, the role assigned to the Golfer entity in the previous chapter using the @Role annotation is defined using the <component> element instead:

  <component name="golferExample"
   class="org.open18.model.Golfer" scope="event"/>

From the standpoint of the application, there’s no difference in how the component is constructed when it’s defined using annotations versus XML. Seam builds the same internal representation of a component definition in both cases and uses it to dish out component instances. But the XML version allows you to assign initial values to bean properties, which is covered in section 5.3. Right now, the XML stanza appears basic because all it’s doing is declaring the component.

 

Warning

You can’t create XML-based component definitions for classes on the hot-deployment classpath (sourced from the src/action folder of WAR projects created by seam-gen). The component scanner that processes the component descriptors can’t “see” classes in the hot-deploy classloader. This shortcoming may be resolved in a future release. Regardless, it defeats the purpose of using the hot-deploy classloader since components defined in component descriptors are not hot deployable (hot-deployable components can only be defined using @Name). The component descriptor can still be used to register initial property values for hot-deployable components.

 

Although the general component descriptor may seem simple enough to manage with just a few component definitions in it, the trouble is that its size can quickly get out of hand as you start relying on it more heavily. Narrowing in on a single configuration becomes as challenging as finding a matching sock in a laundry pile. To prevent this melting pot of configurations, let’s consider how to segment declarations by component class using fine-grained component descriptors.

5.1.3. Fine-grained component descriptors

The fine-grained component descriptor is designed to make it more intuitive for the developer to locate the configuration for a class, since it’s adjacent to the class it configures, and to make the content of that descriptor be “task-oriented” since it focuses on a single class. It also offers a nice alternative to using Seam component annotations—especially if you shiver at the thought of using a lot of annotations on your class—without losing the benefit of being in close proximity to the class.

Fine-grained descriptors are identified by the .component.xml file extension and are used to configure a neighboring Java (or Groovy) class as a Seam component. The name of the class to which the fine-grained descriptor corresponds is derived by stripping the .component.xml extension from the descriptor’s resource path and converting slashes (/) to dots (.). For example, the fine-grained component descriptor whose resource path is org/open18/auth/PasswordBean.component.xml is used to configure the org.open18.auth.PasswordBean class. The reverse logic is used to derive the resource path of the fine-grained descriptor from the name of a class. Seam searches in both directions when preparing the component definition.

You saw this dispersed approach to XML-based configuration in chapter 3 when you were introduced to fine-grained page descriptors. They differ in that a fine-grained page descriptor only deals with a single <page> element, whereas the fine-grained component descriptor is capable of accepting one or more <component> elements, depending on whether the root tag is <component> or <components>. If you intend on declaring only a single component definition, you use <component> as the root element. A fine-grained descriptor with a single component definition need not declare the class attribute, as the class name is derived according to the conversion logic just described. The content of a fine-grained descriptor is shown here, which includes the optional XML namespace declarations:

   <component xmlns="http://jboss.com/products/seam/components"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
     http://jboss.com/products/seam/components
     http://jboss.com/products/seam/components-2.0.xsd"
    name="passwordBean" scope="event"/>

If you intend on using multiple declarations in the fine-grained descriptor, you make <components> the root element. However, by not using <component> as the root element, you lose the benefit of the implied value of the class attribute, which is now required. What you do gain is the full capacity of the component descriptor for configuring the class. You can assign one or more roles using multiple <component> elements, or you can use auxiliary elements like <factory> and <event>, which are both covered in the next chapter.

 

Warning

There’s nothing stopping you from putting arbitrary component definitions—definitions not related to the adjacent class—in a fine-grained descriptor that uses <components> as the root element. However, this practice is discouraged because it hides component definitions in unexpected places.

 

The downside of the fine-grained descriptor is that it is yet another XML file that you have to manage. You also lose the type safety that annotations afford you. Fortunately, there’s a compromise. Seam offers “type-safe” XML elements through the use of XML namespaces and XML Schema, thus reducing the pain involved in working with XML.

5.2. XML namespaces in the component descriptor

You can hardly ignore the XML namespace declarations at the top of the component descriptors presented thus far. In fact, they account for more than half of the characters in the documents! Let’s see what this gratuitous metadata is all about and what it buys you.

5.2.1. The purpose of XML namespace declarations

The namespace declarations that attach to the root element of the component descriptor import a vocabulary of XML elements and attributes defined in W3C XML Schema for creating and configuring components. The reason XML Schema is used is because it offers a rich typing system and allows the vocabulary to be extended through custom namespaces (akin to a Java package). That means the names, classes, and bean properties of components can be reflected in the names of the XML elements and attributes, and that strict validation of the markup can be enforced. It’s for this reason that the XML is considered “type-safe.”

Don’t Be So Generic

The http://jboss.com/products/seam/components namespace represents the generic Seam component vocabulary, which provides the <component> element already covered. Aside from the root element, <components>, the other elements in this namespace are <property> (for setting a property value), <import> (which we examine later in this chapter), and <factory> and <event>, both of which are described in the next chapter. Using this namespace alone, you don’t see much benefit from using XML Schema over a less verbose alternative like DTD because the property names in generic <component> definitions can’t be validated. The benefit comes in the extensive set of component-specific namespaces that Seam provides that widen this vocabulary and make it type safe. You can also define your own XML namespaces, as you’ll learn to do later in this section.

 

Note

Each namespace that you import provides an XML vocabulary that maps one-to-one with the names of component classes and their bean properties. Therefore, I’ll refer to the namespaces from this point forward as component XML namespaces.

 

Let’s look at an example where an element from a component XML namespace is used to replace a generic element. The built-in component named org.jboss.seam.core.init has a property named debug that controls Seam’s debug mode. With the generic component namespace already declared, the debug mode property is set to true using the following stanza, which references this component by its name (not class):

  <component name="org.jboss.seam.core.init">
   <property name="debug">true</property>
  </component>

Instead of using generic elements to define or configure components, you can use custom elements and attributes imported from a component XML namespace. The vocabulary associated with the built-in http://jboss.com/products/seam/core namespace includes the XML element <init> (which maps to the Seam component class org.jboss.seam.core.Init) and a set of XML attributes (which fit to the properties of the class). By importing this namespace into the component descriptor and binding it to the namespace alias core, it’s possible to use the qualified <core:init> element to set the debug property of the corresponding component to true, shown here in bold:

  <components xmlns="http://jboss.com/products/seam/components"
   xmlns:core="http://jboss.com/products/seam/core"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
    http://jboss.com/products/seam/components
    http://jboss.com/products/seam/components-2.0.xsd
    http://jboss.com/products/seam/core
    http://jboss.com/products/seam/core-2.0.xsd">
   <core:init debug="true"/>
  </components>

This declaration assigns an initial value to the property of a built-in component, which you’ll learn more about in section 5.3. The key point is that both the property name and value are validated by the schema. Although the namespace declarations are quite verbose, they help cut down on the number of characters needed throughout the remainder of the component descriptor.

 

Info

If you aren’t accustomed to XML Schema-based configuration, you may be turned off by the clutter they introduce to the root element. However, these formalities are your ticket to a friendly development experience. The xsi:schemaLocation attribute maps the XML namespaces to XML Schema Documents (XSDs), which the IDE retrieves and interprets to provide you with XML tag completion, similar to what you get with Java syntax. If you don’t need the IDE support, you can leave off the namespace declarations.

 

Most built-in Seam components are associated with a namespace, which we explore next. After a survey of the built-in namespaces, we tackle the mapping between XML elements in a component namespace and Java classes.

A Survey of Seam’s Built-in Components

An itemization of all of the built-in components in Seam would be in vain, as they are ever-changing. I want to at least give you a snapshot of the functional areas of Seam. Table 5.3 lists Seam’s built-in namespaces along with a description of the components they include.

Table 5.3. Built-in component XML namespaces

Namespace URI / schema location

Purpose

http://jboss.com/products/seam/async http://jboss.com/products/seam/async-2.0.xsd Asynchronous dispatchers
http://jboss.com/products/seam/bpm http://jboss.com/products/seam/bpm-2.0.xsd jBPM integration
http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.0.xsd Generic component definitions, factories, event observers, and context variable prefix imports
http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.0.xsd Core Seam settings (debug mode, transaction management switch, etc.)
http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.0.xsd Drools configuration and security rules
http://jboss.com/products/seam/framework http://jboss.com/products/seam/framework-2.0.xsd Seam application (CRUD) framework
http://jboss.com/products/seam/international http://jboss.com/products/seam/international-2.0.xsd Locale and time zone selector components
http://jboss.com/products/seam/jms http://jboss.com/products/seam/jms-2.0.xsd JMS integration
http://jboss.com/products/seam/mail http://jboss.com/products/seam/mail-2.0.xsd E-mail integration and connection settings
http://jboss.com/products/seam/navigation http://jboss.com/products/seam/navigation-2.0.xsd Global navigation rules and resource locations for global page descriptors
http://jboss.com/products/seam/pdf http://jboss.com/products/seam/pdf-2.0.xsd PDF document storage and key-store configuration for signed PDFs (requires jboss-seam-pdf.jar)
http://jboss.com/products/seam/persistence http://jboss.com/products/seam/persistence-2.0.xsd Persistence units and manager configurations
http://jboss.com/products/seam/remoting http://jboss.com/products/seam/remoting-2.0.xsd JavaScript remoting settings
http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.0.xsd Identity (authentication and authorization) configuration
http://jboss.com/products/seam/spring http://jboss.com/products/seam/spring-2.0.xsd Spring integration (requires jboss-seam-spring.jar)
http://jboss.com/products/seam/theme http://jboss.com/products/seam/theme-2.0.xsd UI theme selector and available themes
http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.0.xsd Transaction providers
http://jboss.com/products/seam/web http://jboss.com/products/seam/web-2.0.xsd Servlet filter configuration

 

Note

The version in the .xsd filename must match Seam’s major version. Thus, if you’re using Seam 2.1.0.GA, the version should be 2.1. Table 5.3 lists the 2.0 versions.

 

To register a component namespace in your component descriptor, first choose a component namespace from table 5.3 and declare it as an XML namespace using an alias of your choice. Next, add the namespace URI and schema location to the xsi:schemaLocation attribute on the root node. Then, you can use tag completion support in the IDE to discover the available components since all of Seam’s built-in namespaces are backed by an XML Schema vocabulary. If you are the exploratory type, I encourage you to just add all of the namespaces from this table and see what your XML editor gives you.

While having fun with tag completion and exploring the built-in components that Seam offers through XML, you may be wondering what these elements have to do with Java classes and component definitions. The first step to understanding this relationship is learning how the namespaces are associated with Java packages.

Namespaces and Java Packages

An XML namespace is a URI—a fancy way of saying a unique name. A namespace looks like a URL, but it’s not mandatory that it resolve to a public document. The namespace is mapped to an alias, such as core in the previous example. The name of the alias is arbitrary. It’s used as a prefix on element names, such as <core:init>, to associate these elements with a particular namespace. The prefix isn’t required for elements in the default namespace, which is set using the xmlns attribute. Typically, component descriptors declare http://jboss.com/products/seam/components as the default namespace. As such, the <component> element doesn’t need a prefix.

Namespaces are similar to Java packages. In fact, Seam enforces a one-to-one relationship between namespaces in the component descriptor and Java packages. You’ll soon learn that the elements are transformations of Java class names and the attributes the bean properties. The use of XML namespaces in the component descriptor is the closest you can get to writing Java without actually using the Java syntax.

Let’s draft an XML namespace for the Open 18 application to replace the use of the generic <component> element. This lesson should also help you understand how elements in a component XML namespace are interpreted so that you can make sense of the syntax used to configure one of Seam’s built-in components.

5.2.2. Defining an XML @Namespace for components in a package

An XML namespace URI can be associated with a Java package using the @Namespace annotation, summarized in table 5.4. @Namespace is a package-level annotation, which means it’s placed above the package declaration in the package-info.java file.[2] When Seam encounters an XML element that’s not in the generic component namespace, it looks for a @Namespace annotation to make the connection between the namespace URI of that element and a Java package. Seam 2.1 supports an implied mapping between namespace URI and Java package, making the @Namespace annotation just a formality. The mechanics of this mapping are addressed in the next section.

2 package-info.java was introduced in Java 5 for declaring package-level annotations and JavaDoc comments.

Table 5.4. The @Namespace annotation

Name:

Namespace

Purpose:

Maps a URI to a Java package. The URI can be used as an XML namespace in the component descriptor. The mapping tells Seam which Java package to look in to find components when processing an XML element in that namespace.

Target:

PACKAGE

Attribute

Type

Function

value String The XML namespace (URI) for this package. Default: none (required).
prefix String A qualifier used to derive the component name from the local name of the XML element, similar to how a Java package qualifies a class name. If this value is empty, a prefix is not used. Default: empty string.

Let’s create a namespace for the authentication package in the Open 18 application. The contents of the package-info.java file in the org.open18.auth package are shown here:

  @Namespace(value="http://open18.org/components/auth")
  @AutoCreate
  package org.open18.auth;
  import org.jboss.seam.annotations.AutoCreate;
  import org.jboss.seam.annotations.Namespace;

Notice that in addition to the @Namespace declaration, other Seam component annotations can be added to the package-info.java file to set defaults for any component in that package. In this case, all components in the org.open18.auth package support the autocreate functionality.

 

Warning

As of Seam 2.0, the component scanner does not pick up @Namespace annotations located on the hot-deploy classpath (sourced from the src/action directory of seam-gen WAR projects). They must be on the main classpath (e.g., the src/model directory).

 

You find similar declarations scattered throughout the Seam code base defining the namespaces shown in table 5.3. Having created a component namespace of our own, let’s see how it’s used to enable domain-specific markup in the component descriptor.

5.2.3. How XML namespaces are interpreted

The @Namespace declaration establishes a link between an XML namespace and a Java package. (Again, in Seam 2.1, this mapping can be implied.) This relationship is the key to extensible XML authoring of components. In other words, you can define your components using custom XML elements just like Seam does its built-in components.

As with Seam’s built-in namespaces, begin by adding the namespace from the @Namespace annotation to the component descriptor. Then choose a namespace alias for associating XML elements with this URI. Listing 5.2 shows the declaration of the http://open18.org/components/auth namespace bound to the auth namespace alias and a definition of a component in the associated Java package.

Listing 5.2. The PasswordBean defined using a component namespace
  <components xmlns="http://jboss.com/products/seam/components"
   xmlns:auth="http://open18.org/components/auth"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="
    http://jboss.com/products/seam/components
    http://jboss.com/products/seam/components-2.0.xsd">
   <auth:password-bean scope="event"/>
  </components>

The <auth:password-bean> element is a type-safe way of declaring the PasswordBean class as a component. You don’t actually have to declare the XML namespace in the root element. You have the option of using the namespace directly in the prefix of the element:

   <http://open18.org/components/auth:password-bean scope="event"/>

The namespace alias is merely a shorthand syntax. Either way, the result is equivalent to what’s achieved using the following generic component definition:

   <component name="passwordBean"
    class="org.open18.auth.PasswordBean" scope="event"/>

Let’s explore how Seam interprets the type-safe declaration to derive the same set of information provided by the generic component definition.

Translating Xml into Java

The translation from the <auth:password-bean> element to a fully qualified Java class is shown in figure 5.1.

Figure 5.1. How Seam interprets an XML element in a component namespace

When Seam encounters an XML element in the auth namespace, it looks to see if there’s a @Namespace annotation with a value that matches the XML namespace URI. Indeed, the namespace maps to a Java package as follows:

  http://open18.org/components/auth -> org.open18.auth

As of Seam 2.1, there are two ways the Java packages can be derived from the XML namespace URI if a matching @Namespace annotation isn’t defined. When the scheme of the URI is http://, Seam implies the package name by stripping the www prefix (if present), reversing the domain name, and appending the trailing paths as subpack-ages (converting slashes to dots):

  http://www.open18.org/auth -> org.open18.auth

When the namespace scheme is java:, rather than http://, the part of the URI that follows the scheme is used as the package name:

  java:org.open18.auth -> org.open18.auth

At this point, the namespace URI has served its purpose and Seam ceases to do anything more with it. It just helps put Seam in the right playing field. Next, the simple name of the class—the class name without the Java package—is derived from the local name of the XML element, which in this case is password-bean. This conversion occurs by making the first letter and any letter after a hyphen uppercase and dropping the hyphens:

   password-bean -> PasswordBean

The package org.open18.auth and the simple class name PasswordBean are assembled to form the fully qualified Java class. The complete translation is as follows:

  <auth:password-bean> -> org.open18.auth.PasswordBean

 

Note

The benefit of using a custom XML element is that it eliminates the need to specify the class attribute on the component definition. Instead, the class is derived by adjoining the Java package assigned to the element’s namespace URL and a translation of the element’s local name from the XML element. If a Java package can’t be resolved from the XML namespace URI of the element, an exception is thrown during deployment that prevents the application from loading.

 

That takes care of the component class, but as you’ve learned, every Seam component must be associated with a name and scope. Let’s see how they are assigned.

Resolving a Component Name and Scope

XML elements in a component namespace are treated as extensions to the <component> element. That means they inherit all of the standard attributes used to define a component that were listed in table 5.2. Because the standard attributes are inherited, the component name and scope can be specified as attributes on the custom element:

   <auth:password-bean name="passwordBean" scope="event"/>

However, declaring the component name on a custom element isn’t necessary. Seam uses the following search order to locate a name to assign to a component defined using a namespace element in the component descriptor:

  • The name attribute on the custom XML element
  • The @Name annotation on the associated Java class
  • A value derived from the local name of the XML element

If a component name is not specified in the name attribute of the XML element or the @Name annotation, Seam gets its hands dirty and derives a name from the Java class. Seam begins by lowercasing the first letter of the simple name of the class to arrive at the unqualified component name:

  PasswordBean -> passwordBean

This is where the @Namespace annotation comes back into play. The @Namespace annotation has a prefix attribute, whose value is used to qualify a component name just as a Java package qualifies a class name. If the prefix attribute on the @Namespace annotation is empty, then the unqualified component name is equivalent to the fully qualified component name. In this example, the prefix attribute is empty, so the component name remains passwordBean.

If the prefix attribute is not empty, the fully qualified component name is constructed by combining the value of the prefix attribute with the unqualified component name, separated by the dot (.) character. Assume for a moment that the namespace had been defined as follows:

  @Namespace(value = "http://open18.org/components/auth",
     prefix = "org.open18.auth")

The component name derived from the <auth:password-bean> declaration becomes org.open18.auth.passwordBean. Don’t confuse this with the fully qualified class name.

To locate a scope, Seam consults the scope attribute on the custom XML element, then checks the @Scope annotation on the class. If neither one is present, a scope is chosen automatically according to table 4.6 in chapter 4.

You should now understand how Seam gets from <core:init> to the built-in Seam component name org.jboss.seam.core.init, shown in an earlier example, knowing that there’s a @Namespace annotation declared on the org.jboss.seam.core package with a prefix of the same name and namespace URI http://jboss.com/products/seam/core. Let’s see how to get the IDE to make this type of association for our component namespace.

Enabling Validation and IDE tag Completion

Declaring the auth namespace alias in your component descriptor isn’t enough to get the XML to validate or to give the IDE the information it needs to provide tag completion. You still need to provide an XML Schema Document (XSD) for each of the component namespaces that you declare. The XML Schema vocabulary for the auth namespace, auth-1.0.xsd, is not shown here, but it’s available in the book source code for this chapter. Once you’ve written that file, you add it to the xsi:schemaLocation attribute in the component descriptor, shown here in bold:

   <components xmlns="http://jboss.com/products/seam/components"
    xmlns:auth="http://open18.org/components/auth"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
     http://open18.org/components/auth
     http://open18.org/components/auth-1.0.xsd
     http://jboss.com/products/seam/components
     http://jboss.com/products/seam/components-2.0.xsd">
    ...
  </components>

If the XSD isn’t available at the URL provided, you need to make the correlation between the auth-1.0.xsd file and its namespace in your XML editor to achieve XML tag completion and validation. This works automatically for Seam’s built-in namespaces since the XSD files are published to a public web server.

Although creating component XML namespaces may appear challenging, the good news is that you are more often a consumer than you are a creator. Most of the time you find yourself using the namespaces included with Seam to configure Seam’s built-in components. Speaking of Seam’s built-in components, you may notice that all of Seam’s components have fully qualified component names to avoid naming conflicts. However, these long component names can be cumbersome to type. Let’s see how to import context variable prefixes so it’s possible to address component instances by their base names.

5.2.4. Importing a context variable prefix

Just as packages are used in Java to avoid naming conflicts between classes, prefixes are used to avoid naming conflicts between component names. A context variable prefix even uses the same dot (.) notation that you’re familiar with from Java packages. To make referring to these names more convenient, Seam offers a way to import a set of qualified context variable prefixes just like the import statement works in Java.

A context variable prefix is imported using the <import> element in the component descriptor so that you can reference the context variable according to its last segment—its unqualified name. Assume for a moment that we assigned the org.open18.auth prefix to the component namespace mapped to the auth XML namespace. The context variable prefix could be imported with the following declaration in the component descriptor:

  <import>org.open18.auth</import>

This import statement applies globally across the application. It allows you to reference the context variable for the PasswordBean component as passwordBean rather than org.open18.auth.passwordBean.

 

Tip

I encourage you to use fully qualified context variable names for your application’s components and then import the context variable prefixes using the <import> tag as needed. This becomes especially important if you are building reusable Seam libraries.

 

All built-in Seam components use qualified component names to be polite and avoid “stealing” context variable names you might need to use. You can find a complete list of Seam’s built-in components in the Seam reference documentation. Given that many of these components are so commonly used, Seam imports their context variable prefixes automatically. At the time of this writing, that list includes the following:

  • org.jboss.seam.bpm
  • org.jboss.seam.captcha
  • org.jboss.seam.core
  • org.jboss.seam.faces
  • org.jboss.seam.framework
  • org.jboss.seam.international
  • org.jboss.seam.jms
  • org.jboss.seam.mail
  • org.jboss.seam.pageflow
  • org.jboss.seam.security
  • org.jboss.seam.security.management (Seam 2.1 or greater)
  • org.jboss.seam.security.permission (Seam 2.1 or greater)
  • org.jboss.seam.theme
  • org.jboss.seam.transaction
  • org.jboss.seam.web

This set of imports is especially convenient for those often-used Seam components. One common component to reference in the UI is the FacesMessages class, bound to the org.jboss.seam.faces.facesMessages context variable. You can see that the namespace used by the context variable appears in the default list of imports, so you can instead reference it using the abbreviated context variable name facesMessages. You may use this component, for example, to iterate over the global JSF messages, without having to use <h:messages> with the globalOnly flag:

   <rich:dataList var="msg" value="#{facesMessages.currentGlobalMessages}">
    #{msg.summary}
   </rich:dataList>

Component names are used as context variables to access Seam components, which you learned about in the previous chapter. They also provide a means of configuring initial property values for a component, which are applied to an instance after it’s created (either by Seam or by a collaborating container). In the next section, you’ll learn how to set the initial state of an instance by supplementing its component definition with property values.

5.3. Configuring component properties

In the previous chapter you learned how a class enters into “component-hood” by way of annotations and, in this chapter, as a result of XML-based declarations. But alone, these definitions are just a fancy way of instantiating a class and weaving services into it. Oftentimes, a component becomes useful only once its state is initialized, which entails assigning initial values to its properties. This initialization comes in the form of a simple property value, such as a connection string, or as a reference to another component, effectively wiring components together. The property value assignments occur prior to the instance being put to work as a context variable.

 

Note

The @Create and @PostConstruct life-cycle callback methods are executed after the initial property values have been assigned to the component instance.

 

In this section, you’ll learn how these initial property values are declared and what types of values can be supplied. Before we get into the mechanics, let’s consider the benefit of establishing the initial state of a component and find out what is meant by a component property.

5.3.1. Component definitions as object prototypes

Where a component definition pays off is when it’s used to produce an object prototype. As part of the component definition, you can store a set of property names and associated values, which Seam picks up on startup and subsequently transfers to the component instance after it’s instantiated. The prototype might serve to prepopulate a form or to fill in fixed values that aren’t modifiable by the user, such as the date a record is created. Let’s see how these properties get mapped to the class.

A property name is mapped to either a JavaBean-style “setter” method or a field on the target object, herein referred to generally as a bean property. The field name shares the same name as the property, whereas the setter method is derived by capitalizing the property name and prefixing it with set (e.g., the createdDate property maps to the setCreatedDate() method). If a property name matches both a field and a setter method, the setter method takes precedence. The property value is then injected into the method or field using reflection. When the dependency being injected is a reference to another component instance, the mechanism is referred to as dependency injection (DI), or more informally “component wiring” to borrow from the Spring term “bean wiring.”

 

Note

The access level of the method or field on the target object doesn’t matter. Seam can assign a value to a method or field of any access level, even if it’s private—a privilege granted to reflection.

 

Unlike other parts of the component definition, component properties must be defined in external configuration, rather than in annotations.[3] Although Seam tries to avoid unnecessary external configuration, namely XML, configurable properties is one case where it makes sense to take advantage of the decoupling. Declaring a property value outside of the Java source code allows you to achieve any of the following:

3 There is one exception to this rule. The @Logger annotation instructs Seam to inject a log instance into the annotated method or field when the component is instantiated.

  • Adjust the runtime behavior of the application without having to recompile (e.g., timeout period, debug mode, maximum number of query results)
  • Define different property values for different component roles
  • Declare references to other component instances, known as component wiring

It’s up to you to decide when and where to use component properties in your application. The next choice to make is where to define that initial property value.

5.3.2. Where component properties are defined

You can add an initial property value to a component definition by declaring it in one of three places, listed in the order of increasing precedence:

  • Component descriptor
  • Servlet context parameter
  • seam.properties

Chances are, you’ll use the component descriptor a vast majority of the time, given that it’s the most flexible and convenient. Earlier you learned to use the component descriptor to define Seam components with either a generic <component> element or an element bound to a component XML namespace. You can use the same elements to assign property metadata, to augment the component definition, or to configure an existing component. Section 5.4 clarifies this distinction.

As an alternative to using the component descriptor, you can configure component properties using the standard Java properties syntax, herein referred to as external property settings. External property settings can be defined in either the seam.properties file or as servlet context parameters. Seam employs a simple naming convention to determine how the property key is mapped to the bean property of a Seam component, which we’ll go over when we look at this technique.

The remainder of this section takes a hands-on approach to explaining how to use the formats just mentioned, drawing on use cases from the Open 18 application. Given that you already have the component descriptor open, we’ll start with the XML-based component configuration.

Defining Properties in the Component Descriptor

Properties can be associated with any component definition declared in the component descriptor. If you use the generic <component> element, the properties are configured using nested <property> elements. The name of the property being configured is specified in the name attribute of the <property> element, and the value to be assigned is specified in the body of the element (or within a nested <value> element).

To associate the configuration properties with an existing component definition, you specify the component’s name in the name attribute of the <component> element. If you want the <component> declaration to also serve as a component definition, you must also supply the class attribute. To learn the distinction between the two, see section 5.4.

Let’s assume that we want to configure the hashing algorithm and character set used in the PasswordManager component, shown in listing 5.3. The digestAlgorithm property determines the type of hash that’s calculated from the plain-text password, and the charset property determines the encoding scheme applied to the password prior to hashing it.

Listing 5.3. A configurable component used to hash plain-text passwords
  package org.open18.auth;

  import java.security.MessageDigest;
  import org.jboss.seam.annotations.Name;
  import org.jboss.seam.util.Hex;

  @Name("passwordManager")
  public class PasswordManager {
     private String digestAlgorithm;
     private String charset;

     public void setDigestAlgorithm(String algorithm) {
        this.digestAlgorithm = algorithm;
     }

     public void setCharset(String charset) {
        this.charset = charset;
     }

     public String hash(String plainTextPassword) {
        try {
           MessageDigest digest =
              MessageDigest.getInstance(digestAlgorithm);
           digest.update(plainTextPassword.getBytes(charset));
           byte[] rawHash = digest.digest();
           return new String(Hex.encodeHex(rawHash));
       }
       catch (Exception e) {
       throw new RuntimeException(e);
      }
     }
   }

Initial property values must be assigned to customize the behavior of the hash() method for the current application (and to prevent a NullPointerException). The following declaration configures the digestAlgorithm and charset values for a component named passwordManager using nested <property> elements:

   <component name="passwordManager">
    <property name="digestAlgorithm">SHA-1</property>
    <property name="charset">UTF-8</property>
   </component>

If there is no component named passwordManager in the Seam container, this configuration serves no purpose. When the passwordManager component name is requested, Seam instantiates a new instance of the corresponding class and then applies the property values using reflection. The net effect is equivalent to the following Java code:

   PasswordManager passwordManager = new PasswordManager();
   passwordManager.setDigestAlgorithm("SHA-1");
   passwordManager.setCharset("UTF-8");
   Contexts.getEventContext().set("passwordManager", passwordManager);

This snippet is intended to provide a general picture of how the property values affect the instance. Of course, this is a generalization of what happens when a component is instantiated. In actuality, there are a plethora of method interceptors that are wrapped around the instance followed by execution of its life-cycle methods, if defined.

To make for a more concise declaration, properties can be defined as attributes on the <component> element. The component configuration for PasswordManager has been modified to take advantage of this shorthand:

  <component name="passwordManager" digestAlgorithm="SHA-1" charset="UTF-8"/>

However, the attribute syntax comes with a very important disclaimer. You can’t use the attribute syntax to configure a property whose name is a reserved word in the XML vocabulary of the <component> element. The reserved words follow; these attribute names are interpreted as part of the component definition:

  • name
  • class
  • scope
  • auto-create
  • installed
  • startupDepends (Seam 2.1 or greater)
  • startup
  • precedence
  • jndi-name

Memorize this list or keep it close at hand. Otherwise, you’ll be very confused as to why you get an error (or a silent failure) when you try to assign a value to a bean property whose name is in this reserved list using an attribute on the <component> element. One way to handle this case is to use a nested <property> (or namespace element). Another option is to use an external property setting, covered later.

The third syntax for registering an initial property value in the component descriptor is to use a nested element whose name is equivalent to the property that you’re configuring. Once again, the component configuration for PasswordManager has been rewritten to reflect this syntax:

  <component name="passwordManager">
   <digestAlgorithm>SHA-1</digestAlgorithm>
   <charset>UTF-8</charset>
  </component>

If you’ve been following along with your XML editor, you know that the XML Schema validator is not happy with these last two variations—using an attribute on <component> or nested element that shares the name of the property. That’s because the generic component XML vocabulary doesn’t declare any attributes or elements with the names digestAlgorithm or charset.

So what good is this syntax if it doesn’t validate? Well, it just doesn’t validate yet. You simply need to educate the XML validator. As you learned, an element in a component XML namespace extends from the generic <component> element. You have to extend the XML Schema of the generic namespace and append any custom attributes or nested element names used by your component namespace. That requires that you create an XSD, as covered in section 5.2.3. Then you can use the element-based syntax to configure the property of a component and still have the document validate.

Granted, if you aren’t concerned about the document validating and just want to make your XML cleaner without the overhead of creating an XSD, then there’s nothing stopping you. Seam doesn’t mandate that the document validate against the schema so as to avoid imposing an arbitrary restriction on you. Use of XSD is merely a best practice to get the type safety when developing. However, be warned that the XML editor may not be so forgiving and may complain loudly about the custom attribute and element names being invalid. Fortunately, all of the built-in Seam components already have corresponding XSDs, so you can use custom attribute and element names to define properties for these components and the document will validate.

Assuming that you’ve imported the appropriate XML vocabulary, then the configuration for the PasswordManager component can take advantage of the shorthand syntax, as shown in listing 5.4, and still validate.

Listing 5.4. Component configuration using custom XML attribute and element names
 <components xmlns="http://jboss.com/products/seam/components"
  xmlns:auth="http://open18.org/components/auth"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
  http://jboss.com/products/seam/components
  http://jboss.com/products/seam/components-2.0.xsd
  http://open18.org/components/auth
  http://open18.org/components/auth-1.0.xsd">
 <auth:password-manager>
  <auth:digest-algorithm>SHA-1</auth:digest-algorithm>
  <auth:charset>UTF-8</auth:charset>
 </auth:password-manager>
</components>

I threw a curve ball at you in that last excerpt. Can you tell what it is? I changed the name of the <digestAlgorithm> element to digest-algorithm. Seam always converts hyphenated element names, attribute names, and the value of the name attribute on the <property> element to their camel-case equivalents. This conversion is consistent with what’s done to derive the simple name of the class from the element name, as you learned in section 5.2.3.

Thus, you could have written the <property> element for the digestAlgorithm property using the hyphenated form:

  <property name="digest-algorithm">SHA-1</property>

or, if the namespace vocabulary supports it, written the attribute name as

  <auth:password-manager digest-algorithm="SHA-1"/>

 

Note

The XML namespace alias (e.g., auth) doesn’t play a role in the processing of elements that map to properties as it does when top-level elements mapped to component classes are interpreted (in the latter case, the alias is used to resolve a Java package as described in Section 5.2.3). In the previous example, you could have used the unqualified element <digest-algorithm> and, disregarding the whining from the XML editor, the property value would have been assigned properly.

 

To summarize, you can configure a component property using a nested <property> element, an attribute with the same name as the property on the component element, or a nested element with the same name as the property. The important question to ask is, “Does it validate?” In all cases, the nested <property> element validates, though without any guarantee of type safety. The component XML namespace vocabulary dictates the custom attributes and nested elements you can use to configure a component’s properties. Seam’s built-in component namespaces mostly rely on the syntax of attribute names in hyphenated form. See Section 5.5 for more examples of this syntax. If you’re defining your own XML Schema, you may choose to adopt Seam’s style as your standard.

That covers the variety of syntaxes you can use to define component properties in XML. Let’s move on to property configuration using external property settings.

External Property Settings

The component descriptor allows you to declare a component and configure its properties. External property settings only allow you to configure the properties of existing Seam components. That means the class must have a @Name annotation or it must be declared as a component in a component descriptor (it must also meet the conditions to be installed).

To define the property of a component using an external property, the property key is constructed by joining the name of the component with the name of the bean property on the component, separated by a dot (.) character. The value associated with that key is used as the value to assign to the bean property. Note that in this case the hyphenated form isn’t acknowledged, so the key must reference the property name verbatim.

Let’s return to our PasswordManager component. For the purpose of this example, we’ll assume that the PasswordManager has been defined as a Seam component named passwordManager. To assign values to the digestAlgorithm and charset properties using external property settings, you add the following two lines to a seam.properties file:

  passwordManager.digestAlgorithm=SHA-1
  passwordManager.charset=UTF-8

If you assigned the fully qualified name org.open18.auth.passwordManager to the PasswordManager component, the assignments appear as follow:

  org.open18.auth.passwordManager.digestAlgorithm=SHA-1
  org.open18.auth.passwordManager.charset=UTF-8

The location of the seam.properties file is described in Section 4.5.1 of Chapter 4. When you define the property settings in the seam.properties file, you specify the value in the standard java.util.Properties syntax, separating the name of the configuration property from the value with either an equals sign (=) or a space. For more complex string values than what is shown here, see the JavaDoc for java.util.Properties for a more complete explanation of the standard rules.

 

Warning

In seam-gen projects, you must use the seam.properties from the resources folder, not the one from src/action or src/model. The latter two locations are ignored by the build.

 

Instead of registering initial property values in a seam.properties file, you can set them up as servlet context parameters in the web application’s WEB-INF/web.xml file. The same java.util.Properties syntax applies. Note that these parameters aren’t defined in the JSF servlet definition, but rather as context-wide initialization parameters using top-level <context-param> elements:

   <context-param>
    <param-name>passwordManager.digestAlgorithm</param-name>
    <param-value>SHA-1</param-value>
   </context-param>
   <context-param>
    <param-name>passwordManager.charset</param-name>
    <param-value>UTF-8</param-value>
   </context-param>

Using servlet context parameters is arguably less flexible since it requires a servlet environment for the properties to take effect. This limitation can make it difficult to create basic unit tests because it forces you to bootstrap the servlet environment.

 

Warning

Keep in mind that the component name (the context variable name) is used as the first half of the property key, not the class name of the component. Many of the built-in Seam components have names that closely resemble the class that they represent, so don’t confuse the two. In addition, don’t confuse the dots in the component name with the final dot used to isolate the name of the bean property.

 

Just to get a taste for a more advanced property key, let’s consider how you might set the property on one of Seam’s built-in components. Figure 5.2 shows how to disable the transaction management in Seam by setting the transactionManagementEnabled property on the built-in org.jboss.seam.init component to false. Transaction management is covered in Chapter 9. For now, we’re merely playing around with the setting for demonstration purposes.

Figure 5.2. How Seam interprets an external property setting in order to assign an initial value to the property of a component

Assigning values to the bean properties of a component using external property settings is a simpler and more terse alternative than XML. In addition, if you define a value for a component property using an external property setting, it takes precedence over the configuration of the same property in the component descriptor. This override can be useful for adjusting property values for different deployment environments.

Those are the basics of configuring components, but your exposure has been limited to basic string properties. Let’s step it up a notch and explore the more complex property types that Seam gives you the capability to configure.

5.3.3. Property value types

The values that are assigned to the properties of a component can be any of the following:

  • Basic types (strings, numbers, Booleans, enums, characters, and class names)
  • EL (value and method expressions)
  • Collections (where each individual value can be any item in this list)
  • Replacement tokens (a name surrounded by @ symbols)

Let’s cover a couple more of the basic types before moving further down the list.

Basic Value Types

The basic types are straightforward. A value begins its life as a string when it’s read from the configuration file. Seam then determines the correct type for the value based on the target property’s type and converts it before performing the assignment. If a converter isn’t registered, an exception is thrown at startup. If the value can’t be converted, an exception is thrown when the component is instantiated.

 

Tip

You can create your own property converter by implementing the Converter interface on org.jboss.seam.util.Conversions and registering it using the static putConverter() method on that class. However, right now you have to subclass SeamListener in order to install the converter before Seam initializes.

 

In the case of the PasswordManager component, no conversion occurs since both properties, digestAlgorithm and charset, are strings. But Seam can handle most of the common conversations that you’d expect to be supported. Let’s look at a couple more examples.

Most golf courses have 18 holes (those that don’t have nine holes). Let’s use component configuration to set a sensible default value for the numHoles property on a transient Course instance:

   <component name="newCourse" class="org.open18.model.Course">
    <property name="numHoles">18</property>
   </component>

The property numHoles is a primitive integer. Seam performs a basic conversation in this case using Integer.valueOf(String). You can expect the same conversion to be done for all types represented by primitive wrapper classes (and thus have the valueOf (String) method). Two additional types that Seam supports are java.lang.Class and java.lang.Enum. A class is derived from a string using Class.forName(String) and an enum is selected by matching the string against the literal value of the constant.

Let’s assume that the type property on the Facility entity has been changed from a string to the FacilityType enum defined as follows:

   public enum FacilityType {
      PUBLIC, PRIVATE, SEMI_PRIVATE, RESORT, MILITARY;
   }

You could set the default type to PUBLIC on the Facility prototype as follows:

   <component name="newFacility" class="org.open18.model.Facility">
    <property name="type">PUBLIC</property>
   </component>

 

Warning

The tricky conversion is that of Booleans, which Seam converts from a string using Boolean.valueOf(String). This method only considers a value true if it matches the string “true,” ignoring case. All other values are interpreted as false.

 

Component configuration gets interesting when the initial value is an EL expression since it’s capable of injecting a dynamic, contextual value. The value may even be an instance of another Seam component. If the Spring-Seam bridge is configured, which is covered in Chapter 15 (online), you can even inject a Spring bean into a Seam component using the EL. Let’s dig into EL property values and review some examples.

Expression Language Values

It’s been pretty much hammered into your brain by this point that Seam relies heavily on the EL as a means of getting a handle on a component instance or other context variable. The EL’s API agnostic syntax is the reason Seam is able to unify such a wide range of technologies in a straightforward way. So it should be no surprise that Seam turns to the EL again for assigning dynamic property values. Using the EL to define property values is a powerful concept for two reasons:

  • You can leverage existing knowledge of the EL.
  • Any value accessible via the EL can be assigned (not just component instances).

That’s right. Rather than inventing yet another XML vocabulary for wiring Java objects together, Seam leverages the EL to establish references between components. You’ll learn about component wiring in the next section. The EL can also be used for calculating a value and injecting the result. There’s really nothing new here, which is a good thing. Let’s explore the mechanics of how the EL is handled as an initial property value and when it is evaluated.

 

Info

In Spring, you have to choose the appropriate XML element depending on what you’re injecting. For instance, to inject a reference to another bean you use <ref> or <bean>. To assign a null value, you use <null>. In Seam, all of those details are handled beneath the EL. A reference to another component is written as #{componentName}, and to assign a null value you use #{null}.

 

When you declare a property value using a value expression, Seam doesn’t evaluate the expression immediately, but instead stores it in the component definition in raw form. When the properties of a new component instance are being initialized, Seam evaluates the value expression, performs any necessary conversion on the resolved value (as described previously), and then assigns the value to the property.

Think about the possibilities that this introduces. You can theoretically have a component whose purpose is to provide a prepopulated contextual instance. For example, when the member registration page is brought up, the transient Golfer instance can be initialized with the current date and time assigned to the dateJoined property, saving the register() method from having to perform this work:

   <component name="newGolfer" class="org.open18.model.Golfer" scope="event">
    <property name="dateJoined">#{currentDatetime}</property>
   </component>

To set the value of the dateJoined property, you could supply any EL expression that generates a java.util.Date. Saving you a few keystrokes, Seam provides a built-in component named currentDatetime that supplies the current date and time—as produced by new java.sql.Timestamp()—when it’s looked up. This component is one of several date-related components in the Seam Application Framework (the others are currentDate and currentTime). You’ll learn about the Seam Application Framework in Chapter 10.

Let’s get a little fancier. Many golf facilities have only one golf course that bears the same name as the facility. Let’s set the name of the new course to the facility name when creating a transient Course instance:

   <component name="newCourse" class="org.open18.model.Course" scope="event">
    <property name="name">#{facilityHome.instance.name}</property>
   </component>

There are several important points to be made about properties defined using value expressions:

  • The component instance never sees the value expression, only the resolved value.
  • The value expression is resolved when the component instance is created.
  • The component instance won’t be notified if the underlying value of the value expression changes after the component instance is created.

If you’ve used the JSF managed-bean facility, you should recognize that this is exactly how JSF deals with value expression injections. While the component configuration stores the expression, only the resolved value is passed on to the property.

There are two exceptions to these rules. If the target property’s type is a ValueExpression or a MethodExpression, Seam won’t evaluate the expression, even when the properties of the component instance are being initialized. Instead, the expression string is converted to an expression object (ValueExpression or MethodExpression) and assigned to the property. The evaluation of the expression is left up to the component. You can see an example of this scenario in the built-in Seam component org.jboss.seam.security.Identity. The authenticateMethod property is of type MethodExpression. The method expression is evaluated in the application logic to perform authentication against the credentials supplied in the login form. Assuming you’ve defined a Seam component named authenticator with the method authenticate() that handles authentication, you wire the authenticate method into the built-in component using the following declaration:

   <security:identity authenticate-method="#{authenticator.authenticate}"/>

The other exception is a bit of an anomaly (perhaps even a bug). EL notation in a property value isn’t interpreted unless the EL appears as the first character. Otherwise, Seam treats the value as a regular string, assigning it to the property unevaluated. It’s the responsibility of the application logic to interpolate the string for any embedded EL expressions after that. For example, you might want to define a contextual message string:

   <framework:created-message>
    You have successfully added #{course.name}.
   <framework:created-message>

One of the main benefits of using the EL is that it’s universal. You’ll learn in Section 5.3.4 how to use this exact syntax to perform static wiring of components. The point I want to make here is that you use a consistent approach for assigning any value, whether it be a simple type, a basic object (e.g., a date), or an EL expression. In fact, next you’ll discover that the same is true for assigning collection and map values. While Seam does introduce elements for building a collection of values, you can alternatively use the EL to handle the assignment.

Collections and Maps

There are two ways to assign a value to a property whose type is a collection, aside from using an EL value expression. You can either use a flat string value, which Seam will automatically slice and dice to extract the values, or you can specify each item explicitly using nested XML elements. Obviously, the second option only works when using the component descriptor. XML is also the only way to assign values to maps. The value of each item can be a basic value or an EL value expression.

 

Warning

In order for you to use component configuration on a collection property, the property must be a parameterized collection or an array. Seam relies on the generic type information in the parameterized collection or the array type to convert the individual values.

 

Let’s begin by looking at collections. Seam converts flat values into collections by splitting on all of the following characters:

  • Comma
  • Space or tab ( )
  • End of line (EOL) character ( , f, )

Seam uses the flat value converter if the property type is a collection (which includes both arrays and java.util.Collection types). The only time Seam doesn’t perform this conversion is if the value is placed within a <value> element nested inside a <property> or custom namespace element. Let’s try some examples, looking first at when the conversion is used and then how to use the <value> element to avoid the conversion.

Assume that the property proStatus has been added to the Golfer entity to capture the golfer’s skill level—amateur, pro, or semi-pro. The available options are stored in the proStatusTypes property on the RegisterAction component:

   @Name("registerAction")
   public class RegisterAction {
      ...
      private String[] proStatusTypes;

      public String[] getProStatusTypes() { return this.proStatusTypes; }
      public void setProStatusTypes(String[] types) {
         this.proStatusTypes = types;
      }
    }

The options can be registered using XML as before. However, now the value is split on the recognized delimiter characters before being assigned to the collection property on the component. Here’s an XML-based example:

   <component name="registerAction">
    <property name="pro-status-types">amateur pro semi-pro</property>
   </component>

The property can also be assigned using an external property setting:

   registerAction.proStatusTypes=amateur pro semi-pro

The options are converted into a list of JSF SelectItem objects in the registration form using Seam’s <s:selectItems> component tag:

  <h:selectOneMenu value="#{newGolfer.proStatus}">
    <s:selectItems var="_status" label="#{_status}" noSelectionLabel=""
     value="#{registerAction.proStatusTypes}"/>
   </h:selectOneMenu>

The string-to-collection converter works great when the values don’t contain any of the delimiter characters. But what happens when one of them does? Let’s consider an example that demonstrates this problem and learn how to work around it.

Assume that the property specialty has also been added to the Golfer entity to capture the golfer’s forte. An array property named specialtyTypes is added to RegisterAction to hold the available options. If any option contains a delimiter character, we’ll have a problem using the flat property value syntax. To work around this situation, we must declare each value using a child <value> element in XML:

   <component name="registerAction" class="org.open18.action.RegisterAction">
    <property name="pro-status-types">amateur pro semi-pro</property>
    <property name="specialtyTypes">
     <value>Driving</value>
     <value>Chipping</value>
     <value>Putting</value>
     <value>Iron play</value>
     <value>Lookin' good</value>
    </property>
</component>

Let’s look at an example of configuring a multivalue property on one of Seam’s built-in components. The following stanza registers a second page descriptor:

   <components xmlns="http://jboss.com/products/seam/components"
    xmlns:navigation="http://jboss.com/products/seam/navigation"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="
     http://jboss.com/products/seam/navigation
     http://jboss.com/products/seam/navigation-2.0.xsd
     http://jboss.com/products/seam/components
     http://jboss.com/products/seam/components-2.0.xsd">
    <navigation:pages>
     <navigation:resources>
      <value>/WEB-INF/pages.xml</value>
     <value>/META-INF/pages.xml</value>
   </navigation:resources>
 </navigation:pages>
</components>

The nested <value> element syntax is used here since it’s enforced by the XML Schema vocabulary. I show this example to make you aware of the fact that the XML Schema for Seam’s built-in namespaces typically requires this formal syntax. The same configuration can be specified in a seam.properties file as follows:

   org.jboss.seam.navigation.pages.resources=/WEB-INF/pages.xml 
   /META-INF/pages.xml

What I really like about how Seam handles multivalue types is that it doesn’t force you to use a special syntax for different types of collections, such as <set> and <list>. Collections are just collections. Maps do necessitate a special configuration element, though, because of the extra dimension.

Seam supports configuration of associative types—or to use the more familiar term, maps. To make this work, you have to use both a <key> element and a <value> element inside the <property> element. Unfortunately, maps can only be configured using XML. Let’s assume that we want to associate codes to each of the specialties listed above. We first have to change the specialtyTypes property to a java.util.Map. Then we can define the key-value pairs using the following stanza:

   <component name="registerAction"
    class="org.open18.action.RegisterAction">
    <property name="pro-status-types">amateur pro semi-pro</property>
    <property name="specialtyTypes">
     <key>DRIVE</key> <value>Driving</value>
     <key>CHIP</key> <value>Chipping</value>
     <key>PUTT</key> <value>Putting</value>
     <key>IRON</key> <value>Iron play</value>
     <key>LOOKS</key> <value>Lookin' good</value>
   </property>
 </component>

You can also make specialtyTypes a java.util.Properties type and the same declaration would work. Seam doesn’t force you to use distinct elements for different types of associative types.

Replacement Tokens

There’s one more level of abstraction that you can use when supplying property values. Instead of using a value or value expression in the property declaration, you can use a replacement token. Tokens are names that are surrounded by @ symbols.[4] The value of a token is read from the components.properties file at the root of the classpath and applied to the component definition. The value of the token can even be an EL expression. Tokens make it easier to customize the values for different environments without having to modify the descriptor itself. Note that the token has to represent the whole property. It won’t work if you try to put the token inline in a string property.

4 In Seam 2.0, using tokenized values in the component descriptor would cause it not to validate. That has been fixed in Seam 2.1 for commonly tokenized properties.

I cite the example that you’ll see most often used in a Seam application: toggling debug mode. Assuming that you have the following property set in the components.properties file:

   debug=true

you can then use this key as a token value in the component descriptor:

  <core:init debug="@debug@"/>

The value of the token can be a value expression, which will be subject to further evaluation:

  debug=#{facesContext.externalContext.request.serverName eq 'localhost'}

Although not as likely, the token could also represent a reference to another component. That brings us to the topic of component wiring. I introduced the EL as a means of assigning a property value, but let’s now look at the implications of those values resolving to Seam component instances.

5.3.4. Wiring components together

The philosophy behind POJO (Plain Old Java Object) development is that components don’t concern themselves with locating references to other components in the midst of a business method call. Instead, those references are provided during initialization of the component through a technique known as dependency injection, which Seam fully supports. You may choose to adopt this approach if you intend to use your components outside of Seam, perhaps in unit tests or because they’re part of a shared model. In addition, it can be a performance optimization since this type of configuration is performed up front rather than recurring during the lifetime of the component.

In dependency injection, a component first declares that it has one or more dependencies by exposing them as bean properties. The bean property provides the type, name, and means of accepting the reference. The dependency may be another component instance or a regular Java object. You then use some mechanism of declaring how the reference is going to be found. The reference is then resolved and injected into the bean property at runtime. Seam supports two styles of component wiring, one that is static and one that is dynamic. Both styles use reflection to assign values to either the fields or properties—setter methods—of a bean. The difference is when it happens. Depending on the style of injection, the value is injected either when the component instance is created or when a method on the component is executed. Let’s sort out the two cases.

Static vs. Dynamic Dependencies

When you use component configuration, you’re performing static dependency injection. With static injection, Seam assigns a value to a field or property of a component instance when it’s created. The value is specified using the EL variant of component configuration. This style of injection is analogous to that used by other lightweight containers, such as Spring and the JSF managed bean facility. The assignment happens once, drawing from resources available at the time the component is instantiated. After that point, the values of the fields and properties of the component instance aren’t affected by this mechanism. What’s done is done. The properties get their shot again when a new instance is created.

Seam also supports a style of dependency injection that’s dynamic. This hook is activated by placing the @In annotation above a field or JavaBean-style property “setter” method. Annotation-based injections are resolved each time a method is invoked on a component. This mechanism is the key to Seam’s inversion of control, which you’ll learn about in depth in the next chapter.

A Static Component Wiring Example

seam-gen applications include a static dependency injection for the purpose of assembling the components that create an EntityManager instance. You’ll learn about Seam’s persistence configuration in Chapter 9. Right now, we’re focusing on the mechanics of the wiring. The component descriptor from the Open 18 application includes the following two stanzas from the persistence namespace:

  <persistence:entity-manager-factory name="open18EntityManagerFactory"        
   persistence-unit-name="open18"/>
  <persistence:managed-persistence-context name="entityManager"     
   entity-manager-factory="#{open18EntityManagerFactory}"
   auto-create="true"/>

The EntityManagerFactory manager component is an application-scoped startup component that bootstraps a JPA EntityManagerFactory, maintains a reference to it, and closes it on application shutdown. The managed persistence context component manages a conversation-scoped extended persistence context (EntityManager instance) for the lifetime of the conversation. Here’s how they’re assembled. When the entityManager context variable is first resolved in a conversation, an instance of the managed persistence context component is created and the EntityManagerFactory component is wired into it, which is then used to create a new EntityManager instance. This wiring only happens once, which works in this case since the reference (a wider-scoped component) is only needed by the persistence manager context (a narrower-scoped component) while it’s being initialized. Static injection is playing a configuration role here.

Using static injection, let’s now wire dependent component instances into the RegisterAction component to remove the burden (and coupling) of it having to look up these references itself. The focus of the logic shifts to registering the user. It also makes the component more testable. In listing 5.5, the dependent components of RegisterAction are exposed as private fields. We leverage the ability of Seam to inject references into private fields to eliminate unnecessary getters and setters.

Listing 5.5. A component that relies on static injection of dependent components
  package org.open18.action;

  import javax.persistence.EntityManager;
  import org.open18.auth.*;
  import org.open18.model.Golfer;
  @Name("registerAction")
  public class RegisterAction { private EntityManager entityManager;
     private FacesMessages facesMessages;
     private PasswordManager passwordManager;
     private Golfer newGolfer;
     private PasswordBean passwordBean;

     public String register() {
        if (!passwordBean.verify()) {
           facesMessages.addToControl("confirm",
              "value does not match password");
           return "failed";
        }
        newGolfer.setPasswordHash(
           passwordManager.hash(passwordBean.getPassword()));
        entityManager.persist(newGolfer);
        facesMessages.add("Welcome to the club, #{newGolfer.name}!");
        return "success";
     }
  }

The next step is to wire the dependent components to the properties of this component in the component descriptor:

  <component name="registerAction">
    <property name="entity-manager">#{entityManager}</property>
    <property name="faces-messages">#{facesMessages}</property>
    <property name="password-manager">#{passwordManager}</property>
    <property name="new-golfer">#{newGolfer}</property>
    <property name="password-bean">#{passwordBean}</property>
  </component>

While this refactoring drastically simplifies the component, there’s still room for improvement. In the next chapter, you’ll replace the XML configuration with annotations.

 

Tip

If you’re familiar with Spring, you might be tempted to use static injection as the primary means of wiring your components together. I don’t recommend that you standardize on this approach. For one, it requires a ridiculous amount of XML. In general, XML should be reserved for infrastructure configuration. Although not applicable in this example, you also must ensure that you aren’t injecting a component from a shorter-term scope into a component in a longer-term scope, as it results in scope impedance. It’s much cleaner, safer, and Seam-esque to wire components together using dynamic injection, declared using the @In annotation, which you’ll learn to do in the next chapter.

 

With all this great knowledge of how to configure the properties of a component, you’re ready to dress up your component prototypes with initial state so that they’ll come into the world mature. However, there’s one more piece of information you need to know about configuring components; without it, you run the risk of creating a conflicting component definition.

5.4. Component definitions vs. component configuration

The component descriptor can be used to define a new component, configure the properties of an existing component, or define a new component and configure its properties simultaneously. You have to be aware of what constitutes a component definition and when the <component> element is merely interpreted as a means of assigning initial property values to a previously defined component. This section clarifies the distinction and gives you strategies to kept the two separated.

5.4.1. Avoiding conflicts with an existing definition

It’s not permissible to have two components defined with the same name and precedence, which was touched on briefly in the previous chapter. For now, let’s assume that the precedence value isn’t being adjusted. Knowing that you can use the component descriptor to both define and configure components, you can get into trouble if Seam inadvertently tries to create a new component definition when your intention is to assign initial property values to an existing component. Here’s the reasoning Seam uses when it processes the declaration. If the <component> element defines both a class attribute and a name attribute, it’s treated as a new component definition. If the @Name annotation is also present on the class in this case, or there’s an equivalent component definition in another descriptor, the following exception is thrown at application startup:

   java.lang.IllegalStateException:
   Two components with the same name and precedence

If either the class attribute or the name attribute is excluded from the <component> element, Seam treats that declaration as supplemental component configuration (for instance, to enable autocreate) or a contribution of initial property values.

If you’re configuring a class that isn’t a Seam component (it doesn’t have the @Name annotation or the value of the @Install annotation is false), no worries. You can define the component using the <component> element, specifying both name and class. However, if the class that you want to configure is already a Seam component (it has a @Name annotation and isn’t disabled by an @Install annotation), then you have to make sure you don’t collide with the existing definition.

On the other hand, a component definition declared using a component namespace element is subject to different rules that protect it from conflicting with an existing definition. There is reasoning behind this special treatment, but it also gives motivation for using component namespaces. When Seam processes a namespace element, the class attribute is implied from the name of the XML element. The same goes for the name attribute if the @Name annotation is absent from the class. Since the developer can no longer prevent violation of the unique class and name constraint, Seam has to intelligently determine the intention of the declaration. If a component definition already exists, Seam assumes that the goal is to assign component properties, not to define a new component. This explains how you’re able to safely configure a built-in Seam component using a custom namespace element without fear of conflicting with the existing component definition that Seam declares using annotations. In cases where a component definition doesn’t already exist, the namespace element is considered a complete component definition.

Let’s consider an example of how to resolve a conflicting component definition. Assume that the PasswordManager has the annotation @Name("passwordManager"). The following XML stanza defines a conflicting Seam component for this component name:

   <component name="passwordManager" class="org.open18.auth.PasswordManager">
    <property name="digestAlgorithm">SHA-1</property>
    <property name="charset">UTF-8</property>
   </component>

You can alter the root tag of this XML stanza so that it doesn’t collide with the existing definition by making one of four changes:

  1. You can use a custom element from the auth component namespace: <auth:password-manager>
  2. You can remove the class attribute: <component name="passwordManager">
  3. You can remove the name attribute: <component class="org.open18.auth.PasswordManager">
  4. You can set a higher precedence: <component name="passwordManager" class="org.open18.auth.PasswordManager" precedence="25">

The last variation overrides the existing definition since its precedence (25) is higher than the default precedence (20). Precedence values were covered in Section 4.5.2 of Chapter 4.

Of course, if you place the @Name annotation on a class and also define a component for that class in the component descriptor using a different component name, the result is two separate component definitions. But then again, you’re no longer configuring an existing component. All that you need to be aware of is that you can’t have two components defined that use the same name and precedence.

5.4.2. Dividing the configuration between annotations and XML

In the event that the component definition is overridden in XML, any attribute from table 5.2 that isn’t defined in the XML declaration is inherited from its annotation equivalent if present on the class. For instance, the XML definition can rely on the scope defined in the @Scope annotation on the class without having to define the scope in the XML. If the annotation @Scope(ScopeType.APPLICATION) were defined on the class (but not @Name), that value would be inherited by the XML component definition as if the scope attribute had been defined on the <component> element. Basically, not all attributes need to be specified in the same place. If a setting is defined both in the annotation and in the XML, the value in the XML always wins. This hybrid approach is one way to override the component definition in classes that you don’t control.

Now that you have studied defining and configuring components in great detail and have been debriefed on how to avoid conflicts in component definitions when using the component descriptor, you should feel comfortable configuring built-in Seam components.

5.5. Configuring and enabling built-in components

The component descriptor is your primary means for configuring Seam. As much as I hate to say that the component descriptor allows you to “program in XML,” the component descriptor allows you to program in XML. Seam provides a lot of glue code for solving common web application problems and for integrating disparate technologies. To take advantage of some of these features, you have to step in and configure that code. This section explores what areas of Seam can be controlled, focusing on Seam’s language support as a case study.

5.5.1. Using the component descriptor to control Seam

The component descriptor allows you to control Seam to accomplish the following goals:

  • Configure Seam runtime settings— Seam has a lot of switches and levers that you can use to control how it functions. To cite a couple of examples, you can enable debug mode, disable transaction management, define the authentication method, customize parameter names used to manage conversations and the conversation timeout period, specify the names of the resource bundles, or configure the available themes. These components act as the central switchboard in Seam.
  • Activate a feature that is disabled by default— Some of the built-in components aren’t useful to all applications (or may depend on an environment that isn’t always available). Seam disables these components by default. The component descriptor gives you an opportunity to enable them. To provide some examples, you can enable jBPM, hook into the email service, or start the Spring container adapter.
  • Customize a component template— Seam provides a number of component templates. These are cookie-cutter components that you can customize for your own application domain. Examples include the EntityManagerFactory and managed persistence context (and Hibernate equivalents), a Seam Application Framework object (Query, Home, and Controller), a JMS topic publisher or message sender, or a Drools rules manager. These classes don’t bear the @Name annotation and therefore aren’t components until you strike them into action by configuring them in the component descriptor.

Occasionally the distinction between these goals becomes blurry, since you may be activating and configuring a component or service at the same time. The main point to take away is that Seam is highly configurable and the component descriptor is your means of controlling that configuration. As you advance through this book, you’ll keep coming back to this means of configuration, particularly to configure Seam’s built-in components.

I want to explore Seam’s resource bundle management as an example of component configuration in practice. You’ll discover that Seam aggregates message keys under a single built-in component. You’ll also learn how you can use component configuration to register your own bundles, and use message keys to assign locale-specific property values to a component.

5.5.2. Configuring Seam’s internationalization support

When an application needs to output a message, the proper approach is to have it use a message key rather than use a hard-coded message string. The application then looks up a value for that message key in the active resource bundle for the current user at runtime. Typically, the keys are used to select locale-specific message strings. Examples include labels, date and time patterns, and currency units. Resource bundles also have applications that extend beyond locale, such as theme parameters and deployment environment settings.

Unfortunately, resource bundles are one of the most notoriously tedious, yet disproportionally trivial mechanisms to configure in a web application. That’s because a Java framework just wouldn’t be complete without internationalization (i18n) support. This aspiration has led to a situation where each framework wants to use its own resource bundle and configuration of that bundle for providing i18n messages. As it has done many times, Seam steps in to mop up this mess, giving you an unified map of all the messages contributed by the various frameworks it integrates, making it worth the effort of using i18n messages to your application. In this section, I’ll explain how resource bundles work and show you how Seam makes them more accessible.

Seam’s Resource Bundle Management

Resource bundles are an application of the Java properties metadata format (i.e., java.util.Properties), which stores metadata in the form of key-value pairs. These pairs are grouped under a common base name, referred to as the bundle name. There are then off-shoots of the bundle name for each locale. Java works from the most specific locale down to the root bundle name to locate a properties file. The name of the file consists of the bundle name, followed by the current locale prefixed with an underscore (_), followed by the .properties extension. If a file for the current locale cannot be located, a file with the name of the bundle followed by the .properties extension is used.[5]

5 For a detailed description of how this works, refer to the JavaDoc for java.util.ResourceBundle: the getBundle() method.

For example, if the bundle were named messages and the current locale was US English, Java would use the following search order to locate the key:

  • messages_en_US.properties
  • messages_en.properties
  • messages.properties

Seam doesn’t do anything special here to reinvent the wheel. Instead, it just aggregates contributions from multiple properties files. Seam bundles all of the following resource bundles together under the built-in component name messages:

  • messages—the default message bundle
  • ValidatorMessages—Hibernate Validator messages (including defaults)
  • javax.faces.Messages—JSF message keys
  • Page-specific bundles defined in a Seam page descriptor

Note that message bundles declared using the <message-bundle> element in the faces-config.xml descriptor aren’t included in this aggregate bundle. To append to this built-in list, you instead declare your custom bundles in a Seam component descriptor. Let’s say that you have a resource bundle named application and you want to tie those message keys into Seam’s bundle. You register it as follows:

   <core:resource-loader bundle-names="messages application"/>

Notice that I included the bundle named messages. If you override the bundleNames property, you must restore the default bundle name, which is messages, if you want it to be included. As an alternative, you could have declared each bundle name using the nested collection syntax:

   <core:resource-loader>
    <core:bundle-names>
     <value>messages</value>
     <value>application</value>
    </core:bundle-names>
   </core:resource-loader>

At this point, the keys from the various bundles are merged into the collection of keys in Seam’s aggregate resource bundle, which you can access using the context variable name messages. Section 13.6 of Chapter 13 shows how to enable multiple languages in your application, how the default locale is chosen, and how to provide the user with control over the locale used for their session. To wrap up this section, I’ll explain how to use the message keys from this aggregate bundle.

Using Message Keys in the Application Logic

The messages component can be used via an EL value expression. For instance, instead of hard-coding the labels in the registration form, resource keys could be used. First, define a key-value pair in the messages.properties (or locale-specific) file:

   registration.firstName=First name

Then reference in the UI as follows:

  #{messages['registration.firstName']}

You can also use message bundle keys to create a localized JSF message using the built-in Seam component named facesMessages. Earlier, we used the following logic to welcome the new golfer to the club:

  FacesMessages().instance().add("Welcome to the club, #{newGolfer.name}!");

Let’s draw on a message key instead. Once again, define a key-value pair in the message.properties (or locale-specific) file:

  registration.welcome=Welcome to the club, {0}!

You then create a JSF message from this key and populate the indexed placeholder:

  facesMessages.addFromResourceBundle("registration.welcome",
     newGolfer.getName());

You also have the option of putting EL notation directly in the message:

   registration.welcome=Welcome to the club, #{newGolfer.name}!

In the next chapter you’ll learn how to inject references to Seam component instances into properties of the class using annotations. You make the Seam ResourceBundle component available to your class by injecting as follows:

  @In ResourceBundle resourceBundle;

You can also inject the java.util.Map of message bundle keys directly:

  @In Map<String, String> messages;

In addition to using the message keys in the application, you can reference them when configuring the properties of a component.

Using Message Keys in Component Configuration

As you learned earlier, you can specify initial property values using EL notation. That means you can assign a locale-specific value to a component property by consulting the messages context variable. For instance, you can register the names of the specialty types respective to the current locale:

   <component name="registerAction">
    <property name="specialtyTypes">
     <key>DRIVE</key> <value>#{messages['specialty.drive']}</value>
     <key>CHIP</key> <value>#{messages['specialty.chip']}</value>
     <key>PUTT</key> <value>#{messages['specialty.putt']}</value>
     <key>IRON</key> <value>#{messages['specialty.iron']}</value>
     <key>LOOKS</key> <value>#{messages['specialty.looks']}</value>
    </property>
  </component>

These specialty keys would be defined in a properties file loaded by the ResourceBundle:

   specialty.drive=Driving
   specialty.chip=Chipping
   specialty.putt=Putting
   specialty.iron=Iron play
   specialty.looks=Lookin' good

And with that, you can finally come up for air because you’ve mastered using the component descriptor for defining and configuring components.

5.6. Summary

Seam minimizes the need for XML but doesn’t eliminate it. In this chapter, you learned that XML can be useful, and in some cases necessary, for defining and configuring Seam components. XML-based component definitions, which you learned are declared in the Seam component descriptor using the <component> element and its namespace-qualified derivatives, are equivalent to those defined using the annotations covered in the previous chapter.

You came to appreciate that Seam’s dedication to XML Schema makes configuring built-in components type-safe and gives you an opportunity to define your own component vocabulary. With component XML namespaces, your declarations can be validated against an XML Schema and the IDE can read the XML Schema to provide tag completion. You saw many examples of these custom elements in use, both for defining components and configuring components properties.

In addition to defining components, you learned how to use both XML and Java properties to establish the initial state of an object after it’s instantiated by the Seam container. You can now distinguish between an XML component definition and a declaration that merely configures an existing component. If you aren’t aware of these differences, you may encounter the error scenario in which two components with the component name and precedence combination are defined.

When you’re configuring a component, you can supply property values not only as primitive values, basic Java objects, collections, and maps, but also as references to other components, a process called “component wiring.” This chapter showed that component wiring is a form of static dependency injection, where the references are established at the time the component is instantiated, consistent with the dependency injection used in Spring. In the next chapter, you’ll learn about dynamic dependency injection provided by bijection, which injects property values each time the component is invoked. You’ll also learn about other types of inversion of control, such as outjection (the other half of bijection), just-in-time context variable creation, component events, and interceptors, which round out the foundation of Seam’s extensible inversion of control. You’ll also get to discover two component descriptor elements left out of this chapter that pertain to inversion of control: <factory> and <event>.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.35.185