SeamFramework.orgCommunity Documentation

Seam Config

Alternative metadata sources for defining and configuration beans


1. Seam Config Introduction
1.1. Getting Started
1.2. The Princess Rescue Example
2. Seam Config XML provider
2.1. XML Namespaces
2.2. Adding, replacing and modifying beans
2.3. Applying annotations using XML
2.4. Configuring Fields
2.4.1. Initial Field Values
2.4.2. Inline Bean Declarations
2.5. Configuring methods
2.6. Configuring the bean constructor
2.7. Overriding the type of an injection point
2.8. Configuring Meta Annotations
2.9. Virtual Producer Fields
2.10. More Information

Seam provides a method for configuring JSR-299 beans using alternate metadata sources, such as XML configuration. (Currently, the XML provider is the only alternative available, though others are planned). Using a "type-safe" XML syntax, it's possible to add new beans, override existing beans, and add extra configuration to existing beans.

No special configuration is required, all that is required is to include the JAR file and the Weld Extensions JAR in your project. For Maven projects, that means adding the following dependencies to your pom.xml:



         <dependency>
            <groupId>org.jboss.seam.config</groupId>
            <artifactId>seam-config-xml</artifactId>
            <version>${seam.config.version}</version>
            <scope>runtime</scope>
         </dependency>

         <dependency>
            <groupId>org.jboss.seam.solder</groupId>
            <artifactId>seam-solder</artifactId>
            <version>${weld.extensions.version}</version>
         </dependency>
      

To take advantage of Seam Config, the first thing we need is some metadata sources in the form of XML files. By default these are discovered from the classpath in the following locations:

The beans.xml file is the preferred way of configuring beans via XML, however it may be possible that some JSR-299 implementations will not allow this, so seam-beans.xml is provided as an alternative.

Let's start with a simple example. Say we have the following class that represents a report:

class Report {

    String filename;
    
    @Inject
    Datasource datasource;
    
    //getters and setters
}

And the following support classes:

interface Datasource {

    public Data getData();
}
@SalesQualifier
class SalesDatasource implements Datasource {
  public Data getData()
  {
    //return sales data
  }
}
class BillingDatasource implements Datasource {
  public Data getData()
  {
    //return billing data
  }
}

Our Report bean is fairly simple. It has a filename that tells the report engine where to load the report definition from, and a datasource that provides the data used to fill the report. We are going to configure up multiple Report beans via xml.

Example 1.1. 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:s(1)="urn:java:ee" 
       xmlns:r(2)="urn:java:org.example.reports">

 	<r:Report>  (3)
 		<s:modifies(4)/>
 		<r:filename(5)>sales.jrxml<r:filename>
 		<r:datasource>
 			<r:SalesQu(6)alifier/>
 		</r:datasource>
  	</r:Report>
  	
 	<r:Report fi(7)lename="billing.jrxml">
 		<s:replaces(8)/>
 		<r:datasource>
 			<s:Inject/(9)>
 			<s:Exact>o(10)rg.example.reports.BillingDatasource</s:Exact>
 		</r:datasource>
  	</r:Report>  	
</beans>
    
         

1

The namespace urn:java:ee is Seam Config's root namespace. This is where the built-in elements and CDI annotations live.

2

There are now multiple namespaces in the beans.xml file. These namespaces correspond to java package names.

The namespace urn:java:org.example.reports corresponds to the package org.example.reports, where our reporting classes live. Multiple java packages can be aggregated into a single namespace declaration by seperating the package names with colons, e.g. urn:java:org.example.reports:org.example.model. The namespaces are searched in the order they are specified in the xml document, so if two packages in the namespace have a class with the same name, the first one listed will be resolved. For more information see Namespaces.

3

The <Report> declaration configures an instance of our Report class as a bean.

4

Beans installed using <s:modifies> read annotations from the existing class, and merge them with the annotations defined via xml. In addition if a bean is installed with <s:modifies> it prevents the original class being installed as a bean. It is also possible to add new beans and replace beans altogether, for more information see Adding, modifying and replacing beans.

5

The <r:filename> element sets the initial value of the filename field. For more information on how methods and fields are resolved see Configuring Methods, and Configuring Fields.

6

The <r:SalesQualifier> element applies the @SalesQualifier to the datasource field. As the field already has an @Inject on the class definition this will cause the SalesDatasource bean to be injected.

7

This is the shorthand syntax for setting a field value.

8

Beans installed using <s:replaces> do not read annotations from the existing class. In addition if a bean is installed with <s:replaces> it prevents the original class being installed as a bean.

9

The <s:Inject> element is needed this bean was installed with <s:replaces>, so annotations are not read from the class definition.

10

The <s:Exact> annotation restricts the type of bean that is availible for injection without using qualifiers. In this case BillingDatasource will be injected. This is provided as part of weld-extensions.


It is possible to both apply qualifiers to and set the initial value of a field. Fields reside in the same namespace as the declaring bean, and the element name must exactly match the field name. For example if we have the following class:

class RobotFactory {

  Robot robot;
}

The following xml will add the @Produces annotation to the robot field:


<my:RobotFactory>
  <my:robot>
    <s:Produces/>
  </my:robot>
</my:RobotFactory/>

Inital field values can be set three different ways as shown below:


<r:MyBean company="Red Hat Inc" />

<r:MyBean>
  <r:company>Red Hat Inc</r:company>
</r:MyBean>

<r:MyBean>
  <r:company>
    <s:value>Red Hat Inc<s:value>
    <r:SomeQualifier/>
  </r:company>
</r:MyBean>

The third form is the only one that also allows you to add annotations such as qualifiers to the field.

It is possible to set Map,Array and Collection field values. Some examples:


<my:ArrayFieldValue>

    <my:intArrayField>
        <s:value>1</s:value>
        <s:value>2</s:value>
    </my:intArrayField>
    
    <my:classArrayField>
        <s:value>java.lang.Integer</s:value>
        <s:value>java.lang.Long</s:value>
    </my:classArrayField>
    
    <my:stringArrayField>
        <s:value>hello</s:value>
        <s:value>world</s:value>
    </my:stringArrayField>
    
</my:ArrayFieldValue>

<my:MapFieldValue>

    <my:map1>
        <s:entry><s:key>1</s:key><s:value>hello</s:value></s:entry>
        <s:entry><s:key>2</s:key><s:value>world</s:value></s:entry>
    </my:map1>
    
    <my:map2>
        <s:e><s:k>1</s:k><s:v>java.lang.Integer</s:v></s:e>
        <s:e><s:k>2</s:k><s:v>java.lang.Long</s:v></s:e>
    </my:map2>
    
</my:MapFieldValue>

Type conversion is done automatically for all primitives and primitive wrappers, Date, Calendar,Enum and Class fields.

The use of EL to set field values is also supported:


<m:Report>
   <m:name>#{reportName}</m:name>
   <m:parameters>
      <s:key>#{paramName}</s:key>
      <s:value>#{paramValue}</s:key>
   </m:parameters>
</m:Report>

Internally field values are set by wrapping the InjectionTarget for a bean. This means that the expressions are evaluated once, at bean creation time.

It is also possible to configure methods in a similar way to configuring fields:

class MethodBean {


   public int doStuff() {
      return 1;
   }
   public int doStuff(MethodValueBean bean) {
      return bean.value + 1;
   }
   
   public void doStuff(MethodValueBean[][] beans) {
      /*do stuff */
   }
}

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://java.sun.com/xml/ns/javaee"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:s="urn:java:ee" 
       xmlns:my="urn:java:org.jboss.seam.config.xml.test.method">
    <my:MethodBean>
    
        <my:doStuff>
            <s:Produces/>
        </my:doStuff>
        
        <my:doStuff>
            <s:Produces/>
            <my:Qualifier1/>
            <s:parameters>
                <my:MethodValueBean>
                    <my:Qualifier2/>
                </my:MethodValueBean>
            </s:parameters>
        </my:doStuff>
        
        <my:doStuff>
            <s:Produces/>
            <my:Qualifier1/>
            <s:parameters>
                <s:array dimensions="2">
                    <my:Qualifier2/>
                    <my:MethodValueBean/>
                </s:array>
            </s:parameters>
        </my:doStuff>
        
    </my:MethodBean>
</beans>

In this instance MethodBean has three methods, all of them rather imaginatively named doStuff.

The first <test:doStuff> entry in the XML file configures the method that takes no arguments. The <s:Produces> element makes it into a producer method.

The next entry in the file configures the method that takes a MethodValueBean as a parameter and the final entry configures a method that takes a two dimensional array ofMethodValueBean's as a parameter. For both these methods a qualifier was added to the method parameter and they were made into producer methods.

Method parameters are specified inside the <s:parameters> element. If these parameters have annotation children they are taken to be annotations on the parameter.

The corresponding Java declaration for the XML above would be:

class MethodBean {

            
    @Produces
    public int doStuff() {/*method body */}            
                
    @Produces
    @Qualifier1
    public int doStuff(@Qualifier2 MethodValueBean param) {/*method body */}
    
    @Produces
    @Qualifier1
    public int doStuff(@Qualifier2 MethodValueBean[][] param) {/*method body */}
}

Array parameters can be represented using the <s:array> element, with a child element to represent the type of the array. E.g. int method(MethodValueBean[] param); could be configured via xml using the following:


<my:method>
    <s:array>
      <my:MethodValueBean/>
    </s:array>
</my:method>