Components and contexts
4.5 A component’s life
4.5.2 When to @Install a component
When the component scanner finds a class annotated with @Name, the default behavior is to make it a component. While the automatic discovery of components is a powerful mechanism, you lose a degree of flexibility over which classes are turned into compo-nents. That’s where the @Install annotation comes into play. This @Install annota-tion, summarized in table 4.8, tells Seam the conditions under which to honor a component declaration. It can also be used to allow a second definition of the same component to override the first. Both cases will be considered in detail.
You have a wide range of prerequisites for controlling the condition under which a component is installed. The most clear-cut is the value attribute on the @Install annotation, which is a boolean that can be used to switch the component on or off.
Table 4.8 The @Install annotation Name: Install
Purpose: Used to define a set of prerequisite conditions necessary for a component declaration to be accepted and the component registered with the Seam container (i.e., installed).
Target: TYPE (class)
Attribute Type Function
value boolean A flag indicating whether to install the component. Subse-quent conditions may still prevent the component from being installed. Default: true.
dependencies String[] The names of other components that must be installed for this component to be installed. Default: none.
classDependencies String[] Classes that must be available on the classpath for this component to be installed. Classes are provided as strings to avoid unnecessary compilation requirements.
Default: none.
genericDependencies Class[] Classes that must be acting as components for this com-ponent to be installed. Default: none.
precedence int A weighted value used to compare against other compo-nents assigned the same name. The component of higher precedence will be installed. Default: 20.
debug boolean Indicates that this component should only be installed when Seam is operating in debug mode. Default: false.
You can further control whether the component is installed by enforcing any of the following prerequisites:
■ The presence of other component definitions, looked up by component name
■ The presence of other component definitions, looked up by class name
■ Classes available on the classpath
■ A weighted precedence value (selects one definition over another for the same component name and precedence combination)
■ The Seam debug mode setting
If the prerequisites are not satisfied, that doesn’t mean that its future as a component is entirely bleak, though. You can still place it back into the ranks of the other compo-nents by declaring it in the component descriptor. Several built-in Seam compocompo-nents are declared using @Install(false), allowing you to enable them as needed. A sam-pling of components include
■ Seam managed persistence context
■ jBPM session factory
■ POJO cache
■ Asynchronous dispatcher (Quartz, EJB 3, Spring)
■ Non-JTA transaction manager
■ JMS topic publisher
■ Spring context loader
Aside from limiting the set of component definitions, conditional installation can be useful for selecting between alternate implementations of a component.
ALTERNATEIMPLEMENTATIONS
There are times when you need to perform different logic to support different imple-mentations of the same API, such as the JSF specification or application server environ-ment. To keep your component clean, void of conditional logic that checks for the presence of an implementation class, you may choose to separate the logic for each implementation into different components and have the appropriate component selected by Seam according to the prerequisites defined in the @Install annotation.
To make use of the @Install annotation in this case, you create two implementation classes and one interface. Then you give the two implementations the same component name, and let the @Install annotation handle which one will be configured based on the presence of a particular JSF implementation class.
You can create a component for the Sun JSF implementation:
@Name("jsfAdapter")
@Install(classDependencies = "com.sun.faces.context.FacesContextImpl") public class SunJsfAdapter implements JsfAdapter {...}
and another for the MyFaces JSF implementation:
@Name("jsfAdapter")
@Install(classDependencies =
"org.apache.myfaces.context.servlet.ServletFacesContextImpl") public class MyFacesJsfAdapter implements JsfAdapter {...}
You can then request the component named jsfAdapter from the Seam container and Seam will return the appropriate implementation for you depending on which FacesContext implementation class is available on the classpath.
How many frameworks completely overlook this type of functionality, forcing you to devise your own solution? Conditional installation is a fundamental part of defining components.
NOTE Seam doesn’t allow EL value expressions to be used in the value attribute of the @Install annotation. However, you can put a replacement token (a name surround by a pair of @ symbols) in the installed attribute on the <component> element and then have your build supply an environ-ment-specific value for that token in the components.properties file.
You’ll learn how to use replacement tokens in the next chapter.
There is another facet to alternate implementations: you can define components that should only be available during development mode, possibly ones that override equiv-alently named production components.
DEBUGMODECOMPONENTS
Another way to control the installation of a component is to tie it to Seam’s debug mode flag. You do so by setting the debug attribute on the @Install annotation to true. Seam’s debug mode is controlled by the debug property on the org.jboss.
seam.core.init component. You enable the debug mode flag by adding the follow-ing declaration to the component descriptor:
<core:init debug="true"/>
You’ll learn how to configure built-in Seam components using XML in the next chap-ter. For now, let’s focus on the effect of this setting. When it’s set to true, Seam activates components that are marked with @Install(debug=true). You can use this flag to swap in components that return canned data or otherwise stub out back-end logic. In debug mode, the debug component has higher priority. When it comes time to deploy to a production environment, the debug component is dis-abled, and if a non-debug component with the same component name exists, it becomes activated.
Speaking of priority, one component definition can be selected over another based on its precedence. A precedence value is required any time you have two com-ponents assigned to the same component name. Let’s see how Seam handles the curve ball of conflicting component definitions.
INSTALLATIONPRECEDENCE
Precedence defines which component definition wins when two components try to occupy the same space—in other words, they have the same component name. A pre-cedence is an integer value assigned to a component using the prepre-cedence attribute on the @Install annotation. The higher the value, the more clout it has. All built-in
Seam components have a precedence of Install.BUILT_IN (0), so they can easily be overridden. If a precedence isn’t defined, it defaults to Install.APPLICATION (20).
With precedence in the picture, the rule is that two components can’t be defined with the same name and precedence value. If this situation occurs, it will cause an excep-tion to be thrown at startup when the component scanner discovers it.
If all the prerequisites are satisfied, the component gets the gig. It has made it into the container. A single class can also produce multiple component definitions with different component names and potentially different scopes. These alternate defini-tions are known as component roles.