SeamFramework.orgCommunity Documentation

Seam Solder

Reference Guide


Introduction
1. Getting Started
1.1. Maven dependency configuration
1.2. Transitive dependencies
1.3. Pre-Servlet 3.0 configuration
I. Extensions and Utilities for Developers
2. Enhancements to the CDI Programming Model
2.1. Preventing a class from being processed
2.1.1. @Veto
2.1.2. @Requires
2.2. @Exact
2.3. @Client
2.4. Named packages
2.5. @FullyQualified bean names
3. Annotation Literals
4. Evaluating Unified EL
5. Resource Loading
5.1. Extending the Resource Loader
6. Logging, redesigned
6.1. JBoss Logging: The foundation
6.2. Solder Logging: Feature set
6.3. Typed loggers
6.4. Native logger API
6.5. Typed message bundles
6.6. Implementation classes
6.6.1. Generating the implementation classes
6.6.2. Including the implementation classes in Arquillian tests
II. Utilities for Framework Authors
7. Annotation and AnnotatedType Utilities
7.1. Annotated Type Builder
7.2. Annotation Instance Provider
7.3. Annotation Inspector
7.4. Synthetic Qualifiers
7.5. Reflection Utilities
8. Obtaining a reference to the BeanManager
9. Bean Utilities
10. Properties
10.1. Working with properties
10.2. Querying for properties
10.3. Property Criteria
10.3.1. AnnotatedPropertyCriteria
10.3.2. NamedPropertyCriteria
10.3.3. TypedPropertyCriteria
10.3.4. Creating a custom property criteria
10.4. Fetching the results
III. Configuration Extensions for Framework Authors
11. Unwrapping Producer Methods
12. Default Beans
13. Generic Beans
13.1. Using generic beans
13.2. Defining Generic Beans
14. Service Handler
IV. XML Configuration
15. XML Configuration Introduction
15.1. Getting Started
15.2. The Princess Rescue Example
16. Solder Config XML provider
16.1. XML Namespaces
16.2. Adding, replacing and modifying beans
16.3. Applying annotations using XML
16.4. Configuring Fields
16.4.1. Initial Field Values
16.4.2. Inline Bean Declarations
16.5. Configuring methods
16.6. Configuring the bean constructor
16.7. Overriding the type of an injection point
16.8. Configuring Meta Annotations
16.9. Virtual Producer Fields
16.10. More Information
V. Exception Handling Framework
17. Exception Handling - Introduction
17.1. How Solder's Exception Handling Works
18. Exception Handling - Usage
18.1. Eventing into the exception handling framework
18.1.1. Manual firing of the event
18.1.2. Using the @ExceptionHandled Interceptor
18.2. Exception handlers
18.3. Exception handler annotations
18.3.1. @HandlesExceptions
18.3.2. @Handles
18.4. Exception chain processing
18.5. Exception handler ordering
18.5.1. Traversal of exception type hierarchy
18.5.2. Handler precedence
18.6. APIs for exception information and flow control
18.6.1. CaughtException
18.6.2. ExceptionStack
19. Exception handling - Advanced Features
19.1. Exception Modification
19.1.1. Introduction
19.1.2. Usage
19.2. Filtering Stack Traces
19.2.1. Introduction
19.2.2. ExceptionStackOutput
19.2.3. StackFrameFilter
19.2.4. StackFrameFilterResult
19.2.5. StackFrame
20. Exception Handling - Framework Integration
20.1. Creating and Firing an ExceptionToCatch event
20.2. Default Handlers and Qualifiers
20.2.1. Default Handlers
20.2.2. Qualifiers
20.3. Supporting ServiceHandlers
20.4. Programmatic Handler Registration
Exception Handling - Glossary
VI. Servlet API Integration
Introduction
21. Installation
21.1. Pre-Servlet 3.0 configuration
22. Servlet event propagation
22.1. Servlet context lifecycle events
22.2. Application initialization
22.3. Servlet request lifecycle events
22.4. Servlet response lifecycle events
22.5. Servlet request context lifecycle events
22.6. Session lifecycle events
22.7. Session activation events
23. Injectable Servlet objects and request state
23.1. @Inject @RequestParam
23.2. @Inject @HeaderParam
23.3. @Inject ServletContext
23.4. @Inject ServletRequest / HttpServletRequest
23.5. @Inject ServletResponse / HttpServletResponse
23.6. @Inject HttpSession
23.7. @Inject HttpSessionStatus
23.8. @Inject @ContextPath
23.9. @Inject List<Cookie>
23.10. @Inject @CookieParam
23.11. @Inject @ServerInfo
23.12. @Inject @Principal
24. Servlet Exception Handling Integration
24.1. Background
24.2. Defining a exception handler for a web request
25. Retrieving the BeanManager from the servlet context

Seam Solder is a library of Generally Useful Stuff (tm), particularly if you are developing an application based on CDI (JSR-299 Java Contexts and Dependency Injection), or a CDI based library or framework.

This guide is split into three parts. Part I, “Extensions and Utilities for Developers” details extensions and utilities which are likely to be of use to any developer using CDI; Part II, “Utilities for Framework Authors” describes utilities which are likely to be of use to developers writing libraries and frameworks that work with CDI; Part III, “Configuration Extensions for Framework Authors” discusses extensions which can be used to implement configuration for a framework

Getting started with Seam Solder is easy. All you need to do is put the API and implementation JARs on the classpath of your CDI application. The features provided by Seam Solder will be enabled automatically.

Some additional configuration, covered at the end of this chapter, is required if you are using a pre-Servlet 3.0 environment.

If you are using Maven as your build tool, first make sure you have configured your build to use the JBoss Community repository, where you can find all the Seam artifacts. Then, add the following single dependency to your pom.xml file to get started using Seam Solder:


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

This artifact includes the combined API and implementation.

Tip

Substitute the expression ${seam.solder.version} with the most recent or appropriate version of Seam Solder. Alternatively, you can create a Maven user-defined property to satisfy this substitution so you can centrally manage the version.

To be more strict, you can use the API at compile time and only include the implementation at runtime. This protects you from inadvertantly depending on an implementation class.


<dependency>
   <groupId>org.jboss.seam.solder</groupId>
   <artifactId>seam-solder-api</artifactId>
   <version>${seam.solder.version}</version>
   <scope>compile</scope>
</dependency>

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

In a Servlet 3.0 or Java EE 6 environment, your configuration is now complete!

In addition, a logger implementation (SLF4J, Log4J, JBoss Log Manager or the JDK core logging facility) is required. Refer to Chapter 6, Logging, redesigned for more information about how logging is handled in Solder.

You're all setup. It's time to dive into all the useful stuff that Seam Solder provides!

Seam Solder provides a number enhancements to the CDI programming model which are under trial and may be included in later releases of Contexts and Dependency Injection.

According to the CDI standard, the @Named annotation assigns a name to a bean equal to the value specified in the @Named annotation or, if a value is not provided, the simple name of the bean class. This behavior aligns is with the needs of most application developers. However, framework writers should avoid trampling on the "root" bean namespace. Instead, frameworks should specify qualified names for built-in components. The motivation is the same as qualifying Java types. The @FullyQualified provides this facility without sacrificing type-safety.

Seam Solder allows you to customize the bean name using the complementary @FullyQualified annotation. When the @FullyQualified annotation is added to a @Named bean type, producer method or producer field, the standard bean name is prefixed with the name of the Java package in which the bean resides, the segments separated by a period. The resulting fully-qualified bean name (FQBN) replaces the standard bean name.

package com.acme;

      
@FullyQualified @Named
public class NamedBean {
   public String getAge()
   {
      return 5;
   }
}

The bean in the previous code listing is assigned the name com.acme.namedBean. The value of its property age would be referenced in an EL expression (perhaps in a JSF view template) as follows:

#{com.acme.namedBean.age}

The @FullyQualified annotation is permitted on a bean type, producer method or producer field. It can also be used on a Java package, in which case all @Named beans in that package get a bean name which is fully-qualified.

@FullyQualified

package com.acme;

If you want to use a different Java package as the namespace of the bean, rather than the Java package of the bean, you specify any class in that alternative package in the annotation value.

package com.acme;

      
@FullyQualified(ClassInOtherPackage.class) @Named
public class CustomNamespacedNamedBean {
   ...
}

Seam Solder provides an extensible, injectable resource loader. The resource loader can provide URLs or managed input streams. By default the resource loader will look at the classpath, and the servlet context if available.

If the resource name is known at development time, the resource can be injected, either as a URL or an InputStream:

   @Inject

   @Resource("WEB-INF/beans.xml")
   URL beansXml;
   @Inject
   @Resource("WEB-INF/web.xml")
   InputStream webXml;

If the resource name is not known, the ResourceProvider can be injected, and the resource looked up dynamically:

   @Inject

   void readXml(ResourceProvider provider, String fileName) {
      InputStream is = provider.loadResourceStream(fileName);
   }

If you need access to all resources under a given name known to the resource loader (as opposed to first resource loaded), you can inject a collection of resources:

   @Inject

   @Resource("WEB-INF/beans.xml")
   Collection<URL> beansXmls;
   @Inject
   @Resource("WEB-INF/web.xml")
   Collection<InputStream> webXmls;

Tip

Any input stream injected, or created directly by the ResourceProvider is managed, and will be automatically closed when the bean declaring the injection point of the resource or provider is destroyed.

If the resource is a Properties bundle, you can also inject it as a set of Properties:

   @Inject

   @Resource("META-INF/aws.properties")
   Properties awsProperties;

Seam Solder brings a fresh perspective to the ancient art of logging. Rather than just giving you an injectable version of the same old logging APIs, Solder goes the extra mile by embracing the type-safety of CDI and eliminating brittle, boilerplate logging statements. The best part is, no matter how you decide to roll it out, you still get to keep your logging engine of choice (for the logging wars will never end!).

Before talking about Solder Logging, you need to first be introduced to JBoss Logging 3. The reason is, JBoss Logging provides the foundation on which Solder's declarative programming model for logging is built. Plus, we have to convince you that you aren't tied to JBoss AS by using it.

JBoss Logging acts as a logging bridge. If you don't add any other logging libraries to your project, it will delegate all logging calls it handles to the logging facility built into the Java platform (commonly referred to as JDK logging). That's nice, because it means your deployment headaches caused by missing logging jars are gone. And you accomplish it all through the use of the Logger type. It has the usual level-based log methods and complimentary ones that provide formatting.

Here's an example of how you obtain a logger and log a basic message:

Logger log = Logger.getLogger(Bean.class);

// log a plain text method
log.debug("I'm using JBoss Logging.");

If you want to use another logging engine, such as SLF4J or Log4J, you just have to add the native library to the deployment. Keep in mind, though, if your application server provides one of these frameworks, it will get choosen instead. On JBoss AS, JBoss Logging will prefer the JBoss LogManager because it's the built-in logging engine. (We are looking into more sophisticated runtime selection of the logging engine).

Here are the providers JBoss Logging supports (and the order in which it looks for them):

  • JBoss LogManager
  • Log4J
  • SLF4J
  • JDK logging

So you get that JBoss Logging is an abtraction. What else is it good for?

JBoss Logging has a facility for formatting log messages, using either the printf syntax or MessageFormat. This makes it possible to use positional parameters to build dynamic log messages based on contextual information.

Logger log = Logger.getLogger(Bean.class);

// log a message formatted using printf-style substitutions
log.infof("My name is %s.", "David");
// log a message formatted using MessageFormat-style substitutions
log.errorv("The license for Seam is the {0}", "APL");

The most significant and distinguishing feature of JBoss Logging is support for typed loggers. A typed logger is an interface that defines methods which serve as logging operations. When a method is invoked on one of these interfaces, the message defined in an annotation on the method is interpolated and written to the underlying logging engine.

Here's an example of a typed logger:

import org.jboss.logging.Message;

import org.jboss.logging.LogMessage;
import org.jboss.logging.MessageLogger;
@MessageLogger
public interface CelebritySightingLog {
    @LogMessage @Message("Spotted celebrity %s!")
    void spottedCelebrity(String name);
}

JBoss Logging has is parallel support for typed message bundles, whose methods return a formatted message rather than log it. Combined, these features form the centerpiece of Solder's logging and message bundle programming model (and a foundation for additional support provided by the Seam international module). After looking at the samples provided so far, don't pull out your IDE just yet. We'll get into the details of typed loggers and how to use them in Solder in a later section.

There you have it! JBoss Logging is a low-level API that provides logging abstraction, message formatting and internationalization, and typed loggers. But it doesn't tie you to JBoss AS!

With that understanding, we'll now move on to what Solder does to turn this foundation into a programming model and how to use it in your CDI-based application.

To define a typed logger, first create an interface, annotated it, then add methods that will act as log operations and configure the message it will print using another annotation:

import org.jboss.seam.solder.messages.Message;

import org.jboss.seam.solder.logging.Log;
import org.jboss.seam.solder.logging.MessageLogger;
@MessageLogger
public interface TrainSpotterLog {
    @Log @Message("Spotted %s diesel trains")
    void dieselTrainsSpotted(int number);
}

We have configured the log messages to use printf-style interpolations of parameters (%s).

You can then inject the typed logger with no further configuration necessary. We use another optional annotation to set the category of the logger to "trains" at the injection point, overriding the default category of the fully-qualified class name of the component receiving the injection:

    @Inject @Category("trains")

    private TrainSpotterLog log;

We log a message by simply invoking a method of the typed logger interface:

   log.dieselTrainsSpotted(7);

The default locale will be used unless overridden. Here we configure the logger to use the UK locale:

    @Inject @Category("trains") @Locale("en_GB")

    private TrainSpotterLog log;

You can also log exceptions.

import org.jboss.seam.solder.messages.Message;

import org.jboss.seam.solder.messages.Cause;
import org.jboss.seam.solder.logging.Log;
import org.jboss.seam.solder.logging.MessageLogger;
@MessageLogger
public interface TrainSpotterLog {
    @Log @Message("Failed to spot train %s")
    void missedTrain(String trainNumber, @Cause Exception exception);
}

You can then log a message with an exception as follows:

try {

    ...
} catch (Exception e) {
    log.missedTrain("RH1", e);
}

The stacktrace of the exception parameter will be written to the log along with the message.

Typed loggers also provide internationalization support. Simply add the @MessageBundle annotation to the logger interface.

If injecting a typed logger seems to "enterprisy" to you, or you need to get a reference to it from outside of CDI, you can use a static accessor method on Logger:

TrainSpotterLog log = Logger.getMessageLogger(TrainSpotterLog.class, "trains");

log.dieselTrainsSpotted(7);

The injected version is a convenience for those who prefer the declarative style of programming. If you are looking for a simpler starting point, you can simply use the Logger directly.

You may have noticed that throughout this chapter, we've only defined interfaces. Yet, we are injecting and invoking them as though they are concrete classes. So where's the implementation?

Good news. The typed logger and message bundle implementations are generated automatically! You'll see this strategy used often in Seam 3. It's declarative programming at its finest (or to an extreme, depending on how you look at it). Either way, it saves you from a whole bunch of typing.

So how are they generated? Let's find out!

The first time you need logging in your application, you'll likely start with the more casual approach of using the Logger API directly. There's no harm in that, but it's certainly cleaner to use the typed loggers, and at the same time leverage the parallel benefits of the typed bundles. So we recommend that as your long term strategy.

Once you are ready to move to the the typed loggers and message bundles, you'll need to generate the concrete implementation classes as part of the build. These classes are generated by using an annotation processor that is provided by Solder and based on the JBoss Logging tools project. Don't worry, setting it up is a lot simpler than it sounds. You just need to do these two simple steps:

  • Set the Java compliance to 1.6 (or better)
  • Add the Solder tooling library to the build classpath

Warning

If you forget to add the annotation processor to your build, you'll get an error when you deploy the application that reports: "Invalid bundle interface (implementation not found)". This error occurs because the concrete implementation classes are missing.

Setting the Java compliance to 1.6 enables any annotation processors on the classpath to be activated during compilation.

If you're using Maven, here's how the configuration in your POM file looks:


<dependencies>
    <!-- Annotation processor for generating typed logger and message bundle classes -->
    <dependency>
        <groupId>org.jboss.seam.solder</groupId>
        <artifactId>seam-solder-tooling</artifactId>
        <scope>provided</scope>
        <optional>true</optional>
    </dependency>
    ...
</dependencies>
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.6</source>
                <target>1.6</target>
            </configuration>
        </plugin>
    </plugins>
</build>

Note

In the future, you can expect IDE plugins like JBoss Tools to setup this configuration automatically.

Here are the classes that will be generated for the examples above:

TrainSpotterLog_$logger.java
TrainSpotterLog_$logger_en_GB.java
TrainMessages_$bundle.java

Classes are generated for each language referenced by an annotation or if there is a .i18n.properties language file in the same package as the interface and has the same root name. For instance, if we wanted to generate a French version of TrainMessages, we would have to create the following properties file in the same package as the interface:

TrainMessages_fr.i18n.properties

Then populate it with the translations (Note the property key is the method name):

noTrainsSpotted=pas de trains repéré en raison de %s

Now the annotation processor will generate the following class:

TrainMessages_$bundle_fr.java

Now you can add typed loggers and message bundles at will (and you won't have to worry about unsatisified dependencies).

Seam Solder provides a number of utilility classes that make working with annotations and AnnotatedTypes easier. This chapter walks you through each utility, and gives you some ideas about how to use it. For more detail, take a look at the JavaDoc on each class.

Seam Solder provides an AnnotatedType implementation that should be suitable for the needs of most portable extensions. The AnnotatedType is created from AnnotatedTypeBuilder, typically in an extension's observer method, as follows:

AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder()

        .readFromType(type, true) /* readFromType can read from an AnnotatedType or a class */
        .addToClass(ModelLiteral.INSTANCE); /* add the @Model annotation */

Here we create a new builder, and initialize it using an existing AnnotatedType. We can then add or remove annotations from the class, and its members. When we have finished modifying the type, we call create() to spit out a new, immutable, AnnotatedType.

AnnotatedType redefinedType = builder.create();

One place this is immensely useful is for replacing the AnnotatedType in an extension that observes the ProcessAnnotatedType event:

public <X> void processAnnotatedType(@Observes ProcessAnnotatedType<X> evt) {

    AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder()
            .readFromType(evt.getAnnotatedType(), true)
            .addToClass(ModelLiteral.INSTANCE);
    evt.setAnnotatedType(builder.create());
}

This type is now effectively annotated with @Model, even if the annotation is not present on the class definition in the Java source file.

AnnotatedTypeBuilder also allows you to specify a "redefinition", which can be applied to the type, a type of member, or all members. The redefiner will receive a callback for any annotations present which match the annotation type for which the redefinition is applied.

For example, to remove the qualifier @Unique from the type and any of its members, use this:

AnnotatedTypeBuilder builder = new AnnotatedTypeBuilder()

        .readFromType(type, true)
        .redefine(Unique.class, new AnnotationRedefiner<Unique>() {
            public void redefine(RedefinitionContext<A> ctx) {
                ctx.getAnnotationBuilder().remove(Unique.class);
            }
        });
AnnotatedType redefinedType = builder.create();

No doubt, this is a key blade in Solder's army knife arsenal of tools. You can quite effectively change the picture of the type metadata CDI discovers when it scans and processes the classpath of a bean archive.

The Annotation Inspector allows you to easily discover annotations which are meta-annotated. For example:

/* Discover all annotations on type which are meta-annotated @Constraint */

Set<Annotation> constraints = AnnotationInspector.getAnnotations(type, Constraint.class, beanManager);
/* Load the annotation instance for @FacesValidator the annotation may declared on the type, */
/* or, if the type has any stereotypes, on the stereotypes */
FacesValidator validator = AnnotationInspector.getAnnotation(
        type, FacesValidator.class, true, beanManager);

The utility methods work correctly on Stereotypes as well. Let's say you're working with a bean that was decorated @Model, running the following example will still show you the underlying @Named

// assuming you have a class..

@Model
public class User {
    ...
}
// Assume type represents the User class
assert AnnotationInspector.isAnnotationPresent(type, Named.class, beanManager);
// Retrieves the underlying @Named instance on the stereotype
Named name = AnnotationInspector.getAnnotation(type, Named.class, true, beanManager);

The search algorithm will first check to see if the annotation is present directly on the annotated element first, then searches within the stereotype annotations on the element. If you only want to search for Annotations on Stereotypes, then you can use either of the methods AnnotationInspector.getAnnotationFromStereotype.

There is an overloaded form of isAnnotationPresent and getAnnotation to control whether it will search on Stereotypes or not. For both of these methods, a search is performed first directly on the element before searching in stereotypes.

When developing a framework that builds on CDI, you may need to obtain the BeanManager for the application, can't simply inject it as you are not working in an object managed by the container. The CDI specification allows lookup of java:comp/BeanManager in JNDI, however some environments don't support binding to this location (e.g. servlet containers such as Tomcat and Jetty) and some environments don't support JNDI (e.g. the Weld SE container). For this reason, most framework developers will prefer to avoid a direct JNDI lookup.

Often it is possible to pass the correct BeanManager to the object in which you require it, for example via a context object. For example, you might be able to place the BeanManager in the ServletContext, and retrieve it at a later date.

On some occasions however there is no suitable context to use, and in this case, you can take advantage of the abstraction over BeanManager lookup provided by Seam Solder. To lookup up a BeanManager, you can extend the abstract BeanManagerAware class, and call getBeanManager():

public class WicketIntegration extends BeanManagerAware {


   public WicketManager getWicketManager() {
      Bean<?> bean = getBeanManager().getBean(Instance.class);
      ... // and so on to lookup the bean
   }
   
}

The benefit here is that BeanManagerAware class will first look to see if its BeanManager injection point was satisified before consulting the providers. Thus, if injection becomes available to the class in the future, it will automatically start the more efficient approach.

Occasionally you will be working in an existing class hierarchy, in which case you can use the accessor on BeanManagerLocator. For example:

public class ResourceServlet extends HttpServlet {


   protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
         throws ServletException, IOException {
      BeanManager beanManager = new BeanManagerLocator().getBeanManager();
      ...
   }
}

If this lookup fails to resolve a BeanManager, the BeanManagerUnavailableException, a runtime exception, will be thrown. If you want to perform conditional logic based on whether the BeanManager is available, you can use this check:

public class ResourceServlet extends HttpServlet {


   protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
         throws ServletException, IOException {
      BeanManagerLocator locator = new BeanManagerLocator();
      if (locator.isBeanManagerAvailable()) {
         BeanManager beanManager = locator.getBeanManager();
         ... // work with the BeanManager
      }
      else {
         ... // work without the BeanManager
      }
   }
}

However, keep in mind that you can inject into Servlets in Java EE 6!! So it's very likely the lookup isn't necessary, and you can just do this:

public class ResourceServlet extends HttpServlet {


   @Inject
   private BeanManager beanManager;
   protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
         throws ServletException, IOException {
      ... // work with the BeanManager
   }
}

Seam Solder provides a number of base classes which can be extended to create custom beans. Seam Solder also provides bean builders which can be used to dynamically create beans using a fluent API.

AbstractImmutableBean

An immutable (and hence thread-safe) bean, whose constructor will substitute specification defaults if null is passed for a particular attribute. Subclasses must implement the create() and destroy() methods.

AbstractImmutableProducer

An immutable (and hence thread-safe) abstract class for creating producers. Subclasses must implement produce() and dispose().

BeanBuilder

A builder for creating immutable beans which can read the type and annotations from an AnnotatedType.

Beans

A set of utilities for working with beans.

ForwardingBean

A base class for implementing Bean which forwards all calls to delegate().

ForwardingInjectionTarget

A base class for implementing InjectionTarget which forwards all calls to delegate().

ForwardingObserverMethod

A base class for implementing ObserverMethod which forwards all calls to delegate().

ImmutableBean

An immutable (and hence thread-safe) bean, whose constructor will substitute specification defaults if null is passed for a particular attribute. An implementation of ContextualLifecycle may be registered to receive lifecycle callbacks.

ImmutableInjectionPoint

An immutable (and hence thread-safe) injection point.

ImmutableNarrowingBean

An immutable (and hence thread-safe) narrowing bean. Narrowing beans allow you to build a general purpose bean (likely a producer method), and register it for a narrowed type (or qualifiers).

ImmutablePassivationCapableBean

An immutable (and hence thread-safe) bean, whose constructor will substitute specification defaults if null is passed for a particular attribute. An implementation of ContextualLifecycle may be registered to receive lifecycle callbacks. The bean implements PassivationCapable, and an id must be provided.

ImmutablePassivationCapableNarrowingBean

An immutable (and hence thread-safe) narrowing bean. Narrowing beans allow you to build a general purpose bean (likely a producer method), and register it for a narrowed type (or qualifiers). The bean implements PassivationCapable, and an id must be provided.

NarrowingBeanBuilder

A builder for creating immutable narrowing beans which can read the type and annotations from an AnnotatedType.

The use of these classes is in general trivially understood with an understanding of basic programming patterns and the CDI specification, so no in depth explanation is provided here. The JavaDoc for each class and method provides more detail.

Properties are a convenient way of locating and working with JavaBean properties. They can be used with properties exposed via a getter/setter method, or directly via the field of a bean, providing a uniform interface that allows you all properties in the same way.

Property queries allow you to interrogate a class for properties which match certain criteria.

Once you have created the PropertyQuery instance, you can add search criteria. Seam Solder provides three built-in criteria types, and it is very easy to add your own. A criteria is added to a query via the addCriteria() method. This method returns an instance of the PropertyQuery, so multiple addCriteria() invocations can be stacked.

After creating the PropertyQuery and setting the criteria, the query can be executed by invoking either the getResultList() or getFirstResult() methods. The getResultList() method returns a List of Property objects, one for each matching property found that matches all the specified criteria:

   List<Property<String>> results = PropertyQueries.<String>createQuery(Foo.class)
     .addCriteria(TypedPropertyCriteria(String.class))
     .getResultList();

If no matching properties are found, getResultList() will return an empty List. If you know that the query will return exactly one result, you can use the getFirstResult() method instead:

   Property<String> result = PropertyQueries.<String>createQuery(Foo.class)
      .addCriteria(NamedPropertyCriteria("bar"))
      .getFirstResult();

If no properties are found, then getFirstResult() will return null. Alternatively, if more than one result is found, then getFirstResult() will return the first property found.

Alternatively, if you know that the query will return exactly one result, and you want to assert that assumption is true, you can use the getSingleResult() method instead:

   Property<String> result = PropertyQueries.<String>createQuery(Foo.class)
      .addCriteria(NamedPropertyCriteria("bar"))
      .getSingleResult();

If no properties are found, or more than one property is found, then getSingleResult() will throw an exception. Otherwise, getSingleResult() will return the sole property found.

Sometimes you may not be interested in read only properties, so getResultList(),getFirstResult() and getSingleResult() have corresponding getWritableResultList(),getWritableFirstResult() and getWritableSingleResult() methods, that will only return properties that are not read-only. This means that if there is a field and a getter method that resolve to the same property, instead of getting a read-only MethodProperty you will get a writable FieldProperty.

Unwrapping producer methods allow you to create injectable objects that have "self-managed"" lifecycles, and are particularly useful if you have need a bean whose lifecycle does not exactly match one of the lifecycle of one of the existing scopes. The lifecycle of the bean is are managed by the bean that defines the producer method, and changes to the unwrapped object are immediately visible to all clients.

You can declare a method to be an unwrapping producer method by annotating it @Unwraps. The return type of the managed producer must be proxyable (see Section 5.4.1 of the CDI specification, "Unproxyable bean types"). Every time a method is called on unwrapped object the invocation is forwarded to the result of calling the unwrapping producer method - the unwrapped object.

For example consider a permission manager (that manages the current permission), and a security manager (that checks the current permission level). Any changes to permission in the permission manager are immediately visible to the security manager.

@SessionScoped

class PermissionManager {
   
   Permission permission;
  
   void setPermission(Permission permission) {
      this.permission=permission;
   }
  
   @Unwraps @Current
   Permission getPermission() {
      return this.permission;
   }
}
@SessionScoped

class SecurityManager {
   
   @Inject @Current
   Permission permission;
  
   boolean checkAdminPermission() {
      return permission.getName().equals("admin");
   }
}

When permission.getName() is called, the unwrapped Permission forwards the invocation of getName() to the result of calling PermissionManager.getPermission().

For example you could raise the permission level before performing a sensitive operation, and then lower it again afterwards:

public class SomeSensitiveOperation {

   
   @Inject
   PermissionManager permissionManager;
  
   public void perform() {
      try {
         permissionManager.setPermission(Permissions.ADMIN);
         // Do some sensitive operation
      } finally {
         permissionManager.setPermission(Permissions.USER);
      }
   }
}

Unwrapping producer methods can have parameters injected, including InjectionPoint (which repreents) the calling method.

Suppose you have a situation where you want to provide a default implementation of a particular service and allow the user to override it as needed. Although this may sound like a job for an alternative, they have some restrictions that may make them undesirable in this situation. If you were to use an alternative it would require an entry in every beans.xml file in an application.

Developers consuming the extension will have to open up the any jar file which references the default bean, and edit the beans.xml file within, in order to override the service. This is where default beans come in.

Default beans allow you to create a default bean with a specified type and set of qualifiers. If no other bean is installed that has the same type and qualifiers, then the default bean will be installed.

Let's take a real world example - a module that allows you to evaluate EL (something that Seam Solder provides!). If JSF is available we want to use the FunctionMapper provided by the JSF implementation to resolve functions, otherwise we just want to use a a default FunctionMapper implementation that does nothing. We can achieve this as follows:

@DefaultBean(type = FunctionMapper.class)

@Mapper
class FunctionMapperImpl extends FunctionMapper {
   @Override
   Method resolveFunction(String prefix, String localName) {
      return null;
   }
}

And in the JSF module:

class FunctionMapperProvider {

   
   @Produces
   @Mapper
   FunctionMapper produceFunctionMapper() {
      return FacesContext.getCurrentInstance().getELContext().getFunctionMapper();
   }
}

If FunctionMapperProvider is present then it will be used by default, otherwise the default FunctionMapperImpl is used.

A producer method or producer field may be defined to be a default producer by placing the @DefaultBean annotation on the producer. For example:

class CacheManager {
   
   @DefaultBean(Cache.class)
   Cache getCache() {
      ...
   }
     
}

Any producer methods or producer fields declared on a default managed bean are automatically registered as default producers, with Method.getGenericReturnType() or Field.getGenericType() determining the type of the default producer. The default producer type can be overridden by specifying @DefaultBean on the producer method or field.

Many common services and API's require the use of more than just one class. When exposing these services via CDI, it would be time consuming and error prone to force the end developer to provide producers for all the different classes required. Generic beans provide a solution, allowing a framework author to provide a set of related beans, one for each single configuration point defined by the end developer. The configuration points specifies the qualifiers which are inherited by all beans in the set.

To illustrate the use of generic beans, we'll use the following example. Imagine we are writing an extension to integrate our custom messaging solution "ACME Messaging" with CDI. The ACME Messaging API for sending messages consists of several interfaces:

MessageQueue
The message queue, onto which messages can be placed, and acted upon by ACME Messaging
MessageDispatcher
The dispatcher, responsible for placing messages created by the user onto the queue
DispatcherPolicy
The dispatcher policy, which can be used to tweak the dispatch policy by the client
MessageSystemConfiguration
The messaging system configuration

We want to be able to create as many MessageQueue configurations's as they need, however we do not want to have to declare each producer and the associated plumbing for every queue. Generic beans are an ideal solution to this problem.

Before we take a look at creating generic beans, let's see how we will use them.

Generic beans are configured via producer methods and fields. We want to create two queues to interact with ACME Messaging, a default queue that is installed with qualifier @Default and a durable queue that has qualifier @Durable:

class MyMessageQueues {

   
   @Produces
   @ACMEQueue("defaultQueue")
   MessageSystemConfiguration defaultQueue = new MessageSystemConfiguration();
   
   @Produces @Durable @ConversationScoped
   @ACMEQueue("durableQueue")
   MessageSystemConfiguration producerDefaultQueue() {
      MessageSystemConfiguration config = new MessageSystemConfiguration();
      config.setDurable(true);
      return config;
   }
}

Looking first at the default queue, in addition to the @Produces annotation, the generic configuration annotation ACMEQueue, is used, which defines this to be a generic configuration point for ACME messaging (and cause a whole set of beans to be created, exposing for example the dispatcher). The generic configuration annotation specifies the queue name, and the value of the producer field defines the messaging system's configuration (in this case we use all the defaults). As no qualifier is placed on the definition, @Default qualifier is inherited by all beans in the set.

The durable queue is defined as a producer method (as we want to alter the configuration of the queue before having Seam Solder use it). Additionally, it specifies that the generic beans created (that allow for their scope to be overridden) should be placed in the conversation scope. Finally, it specifies that the generic beans created should inherit the qualifier @Durable.

We can now inject our generic beans as normal, using the qualifiers specified on the configuration point:

class MessageLogger {

  
   @Inject
   MessageDispatcher dispatcher;
  
   void logMessage(Payload payload) {
      /* Add metaddata to the message */
      Collection<Header> headers = new ArrayList<Header>();
      ... 
      Message message = new Message(headers, payload);
      dispatcher.send(message);
   }
  
}
class DurableMessageLogger {

  
   @Inject @Durable
   MessageDispatcher dispatcher;
   
   @Inject @Durable
   DispatcherPolicy policy;
   
         
   /* Tweak the dispatch policy to enable duplicate removal */
   @Inject
   void tweakPolicy(@Durable DispatcherPolicy policy) {
      policy.removeDuplicates();
   }
  
   void logMessage(Payload payload) {
      ...
   }
}

It is also possible to configure generic beans using beans by sub-classing the configuration type, or installing another bean of the configuration type through the SPI (e.g. using Seam XML). For example to configure a durable queue via sub-classing:

@Durable @ConversationScoped

@ACMEQueue("durableQueue")
class DurableQueueConfiguration extends MessageSystemConfiguration {
      
   public DurableQueueConfiguration()
   {
      this.durable = true;
   }
}

And the same thing via Seam XML:


<my:MessageSystemConfiguration>
   <my:Durable/>
   <s:ConversationScoped/>
   <my:ACMEQueue>durableQueue</my:ACMEQueue>
   <my:durable>true</my:durable>
</my:MessageSystemConfiguration>

Having seen how we use the generic beans, let's look at how to define them. We start by creating the generic configuration annotation:

@Retention(RUNTIME)

@GenericType(MessageSystemConfiguration.class)
@interface ACMEQueue {
   String name();
   
}

The generic configuration annotation a defines the generic configuration type (in this case MessageSystemConfiguration); the type produced by the generic configuration point must be of this type. Additionally it defines the member name, used to provide the queue name.

Next, we define the queue manager bean. The manager has one producer method, which creates the queue from the configuration:

@GenericConfiguration(ACMEQueue.class) @ApplyScope

class QueueManager {
   @Inject @Generic
   MessageSystemConfiguration systemConfig;
   
   @Inject
   ACMEQueue config;
   
   MessageQueueFactory factory;
   
   @PostConstruct
   void init() {
      factory = systemConfig.createMessageQueueFactory();
   }
   
   @Produces @ApplyScope
   public MessageQueue messageQueueProducer() {
      return factory.createMessageQueue(config.name());
   }
}

The bean is declared to be a generic bean for the @ACMEQueue generic configuration type annotation by placing the @GenericConfiguration annotation on the class. We can inject the generic configuration type using the @Generic qualifier, as well the annotation used to define the queue.

Placing the @ApplyScope annotation on the bean causes it to inherit the scope from the generic configuration point. As creating the queue factory is a heavy operation we don't want to do it more than necessary.

Having created the MessageQueueFactory, we can then expose the queue, obtaining its name from the generic configuration annotation. Additionally, we define the scope of the producer method to be inherited from the generic configuration point by placing the annotation @ApplyScope on the producer method. The producer method automatically inherits the qualifiers specified by the generic configuration point.

Finally we define the message manager, which exposes the message dispatcher, as well as allowing the client to inject an object which exposes the policy the dispatcher will use when enqueing messages. The client can then tweak the policy should they wish.

@Generic(ACMEQueue.class)

class MessageManager {
   @Inject @Generic
   MessageQueue queue;
   
   @Produces @ApplyScope
   MessageDispatcher messageDispatcherProducer() {
      return queue.createMessageDispatcher();
   }
   
   @Produces
   DispatcherPolicy getPolicy() {
      return queue.getDispatcherPolicy();
   }   
   
}

The service handler facility allow you to declare interfaces and abstract classes as automatically implemented beans. Any call to an abstract method on the interface or abstract class will be forwarded to the invocation handler for processing.

If you wish to convert some non-type-safe lookup to a type-safe lookup, then service handlers may be useful for you, as they allow the end user to map a lookup to a method using domain specific annotations.

We will work through using this facility, taking the example of a service which can execute JPA queries upon abstract method calls. First we define the annotation used to mark interfaces as automatically implemented beans. We meta-annotate it, defining the invocation handler to use:

@ServiceHandlerType(QueryHandler.class)

@Retention(RUNTIME)
@Target({TYPE})
@interface QueryService {}

We now define an annotation which provides the query to execute:

@Retention(RUNTIME)

@Target({METHOD})
@interface Query {
   String value();
   
}

And finally, the invocation handler, which simply takes the query, and executes it using JPA, returning the result:

class QueryHandler {

   
   @Inject EntityManager em;
  
   @AroundInvoke
   Object handle(InvocationContext ctx) {
      return em.createQuery(ctx.getMethod().getAnnotation(Query.class).value()).getResultList();
   }
}

Finally, we can define (any number of) interfaces which define our queries:

@QueryService

interface UserQuery {
   
   @Query("select u from User u");
   public List<User> getAllUsers();
}

Finally, we can inject the query interface, and call methods, automatically executing the JPA query.

class UserListManager {

   @Inject 
   UserQuery userQuery;
  
   List<User> users;
   
   @PostConstruct
   void create() {
      users=userQuery.getAllUsers();
   }
}

Solder provides a method for configuring CDI beans using alternate metadata sources, such as XML configuration. Currently, the XML provider is the only alternative available. Using a "type-safe" XML syntax, it is possible to add new beans, override existing beans, and add extra configuration to existing beans.

To take advantage of XML Configuration, you need 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 some CDI implementations will not allow this, so seam-beans.xml is provided as an alternative.

Here is a simple example. The following class 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
  }
}

The 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 15.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 the XML 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 the 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 the 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 as 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.solder.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 example, MethodBean has three methods. They are all 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 ofMethodValueBeans as a parameter. For both of 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>
      

Exceptions are a fact of life. As developers, we need to be prepared to deal with them in the most graceful manner possible. Solder's exception handling framework provides a simple, yet robust foundation for modules and/or applications to establish a customized exception handling process. By employing a delegation model, Solder allows exceptions to be addressed in a centralized, extensible and uniform manner.

In this guide, we'll explore the various options you have for handling exceptions using Solder, as well as how framework authors can offer Solder exception handling integration.

The entire exception handling process starts with an event. This helps keep your application minimally coupled to Solder, but also allows for further extension. Exception handling in Solder is all about letting you take care of exceptions the way that makes the most sense for your application. Events provide this delicate balance.

There are three means of firing the event to start the exception handling process:

Exception handlers are contained within exception handler beans, which are CDI beans annotated with @HandlesExceptions. Exception handlers are methods which have a parameter which is an instance of CaughtException<T extends Throwable> annotated with the @Handles annotation.

@Handles is a method parameter annotation that designates a method as an exception handler. Exception handler methods are registered on beans annotated with @HandlesExceptions. Solder will discover all such methods at deployment time.

Let's look at an example. The following method is invoked for every exception that Solder processes and prints the exception message to stout. (Throwable is the base exception type in Java and thus represents all exceptions).

(1)@HandlesExceptions

public class MyHandlers
{
(2)   void printExceptions(@Handles CaughtException<Throwable> evt)
   {
      System.out.println("Something bad happened: " +
(3)            evt.getException().getMessage());
(4)      evt.markHandled();
   }
}
            

1

The @HandlesExceptions annotation signals that this bean contains exception handler methods.

2

The @Handles annotation on the first parameter designates this method as an exception handler (though it is not required to be the first parameter). This parameter must be of type CaughtException<T extends Throwable>, otherwise it's detected as a definition error. The type parameter designates which exception the method should handle. This method is notified of all exceptions (requested by the base exception type Throwable).

3

The CaughtException instance provides access to information about the exception and can be used to control exception handling flow. In this case, it's used to read the current exception being handled in the exception chain, as returned by getException().

4

This handler does not modify the invocation of subsequent handlers, as designated by invoking markHandled() on CaughtException. As this is the default behavior, this line could be omitted.

The @Handles annotation must be placed on a parameter of the method, which must be of type CaughtException<T extends Throwable>. Handler methods are similar to CDI observers and, as such, follow the same principles and guidelines as observers (such as invocation, injection of parameters, qualifiers, etc) with the following exceptions:

In addition to designating a method as exception handler, the @Handles annotation specifies two pieces of information about when the method should be invoked relative to other handler methods:

Let's take a look at more sophisticated example that uses all the features of handlers to log all exceptions.

(1)@HandlesExceptions

public class MyHandlers
{
(2)   void logExceptions(@Handles(during = TraversalMode.BREADTH_FIRST)
(3)         @WebRequest CaughtException<Throwable> evt,
(4)         Logger log)
   {
      log.warn("Something bad happened: " + evt.getException().getMessage());
   }
}
            

1

The @HandlesExceptions annotation signals that this bean contains exception handler methods.

2

This handler has a default precedence of 0 (the default value of the precedence attribute on @Handles). It's invoked during the breadth first traversal mode. For more information on traversal, see the section Section 18.5.1, “Traversal of exception type hierarchy”.

3

This handler is qualified with @WebRequest. When Solder calculates the handler chain, it filters handlers based on the exception type and qualifiers. This handler will only be invoked for exceptions passed to Solder that carry the @WebRequest qualifier. We'll assume this qualifier distinguishes a web page request from a REST request.

4

Any additional parameters of a handler method are treated as injection points. These parameters are injected into the handler when it is invoked by Solder. In this case, we are injecting a Logger bean that must be defined within the application (or by an extension).

A handler is guaranteed to only be invoked once per exception (automatically muted), unless it re-enables itself by invoking the unmute() method on the CaughtException instance.

Handlers must not throw checked exceptions, and should avoid throwing unchecked exceptions. Should a handler throw an unchecked exception it will propagate up the stack and all handling done via Solder will cease. Any exception that was being handled will be lost.

When an exception is thrown, chances are it's nested (wrapped) inside other exceptions. (If you've ever examined a server log, you'll appreciate this fact). The collection of exceptions in its entirety is termed an exception chain.

The outermost exception of an exception chain (e.g., EJBException, ServletException, etc) is probably of little use to exception handlers. That's why Solder doesn't simply pass the exception chain directly to the exception handlers. Instead, it intelligently unwraps the chain and treats the root exception cause as the primary exception.

The first exception handlers to be invoked by Solder are those that match the type of root cause. Thus, instead of seeing a vague EJBException, your handlers will instead see an meaningful exception such as ConstraintViolationException. This feature, alone, makes Solder's exception handling a worthwhile tool.

Solder continues to work through the exception chain, notifying handlers of each exception in the stack, until a handler flags the exception as handled. Once an exception is marked as handled, Solder stops processing the exception. If a handler instructed Solder to rethrow the exception (by invoking CaughtException#rethrow(), Solder will rethrow the exception outside the Solder exception handling infrastructure. Otherwise, it simply returns flow control to the caller.

Consider a exception chain containing the following nested causes (from outer cause to root cause):

Solder will unwrap this exception and notify handlers in the following order:

If there's a handler for PersistenceException, it will likely prevent the handlers for EJBException from being invoked, which is a good thing since what useful information can really be obtained from EJBException?

While processing one of the causes in the exception chain, Solder has a specific order it uses to invoke the handlers, operating on two axes:

We'll first address the traversal of the exception type hierarchy, then cover relative handler precedence.

Solder doesn't simply invoke handlers that match the exact type of the exception. Instead, it walks up and down the type hierarchy of the exception. It first notifies least specific handler in breadth first traversal mode, then gradually works down the type hierarchy towards handlers for the actual exception type, still in breadth first traversal. Once all breadth first traversal handlers have been invoked, the process is reversed for depth first traversal, meaning the most specific handlers are notified first and Solder continues walking up the hierarchy tree.

There are two modes of this traversal:

By default, handlers are registered into the DEPTH_FIRST traversal path. That means in most cases, Solder starts with handlers of the actual exception type and works up towards the handler for the least specific type.

However, when a handler is registered to be notified during the BREADTH_FIRST traversal, as in the example above, Solder will notify that exception handler before the exception handler for the actual type is notified.

Let's consider an example. Assume that Solder is handling the SocketException. It will notify handlers in the following order:

The same type traversal occurs for each exception processed in the chain.

In order for a handler to be notified of the IOException before the SocketException, it would have to specify the BREADTH_FIRST traversal path explicitly:



void handleIOException(@Handles(during = TraversalMode.BREADTH_FIRST)
      CaughtException<IOException> evt)
{
   System.out.println("An I/O exception occurred, but not sure what type yet");
}
         

BREADTH_FIRST handlers are typically used for logging exceptions because they are not likely to be short-circuited (and thus always get invoked).

When Solder finds more than one handler for the same exception type, it orders the handlers by precedence. Handlers with higher precedence are executed before handlers with a lower precedence. If Solder detects two handlers for the same type with the same precedence, it detects it as an error and throws an exception at deployment time.

Let's define two handlers with different precedence:



void handleIOExceptionFirst(@Handles(precedence = 100) CaughtException<IOException> evt)
{
   System.out.println("Invoked first");
}
void handleIOExceptionSecond(@Handles CaughtException<IOException> evt)
{
   System.out.println("Invoked second");
}
         

The first method is invoked first since it has a higher precedence (100) than the second method, which has the default precedence (0).

To make specifying precedence values more convenient, Solder provides several built-in constants, available on the Precedence class:

To summarize, here's how Solder determines the order of handlers to invoke (until a handler marks exception as handled):

There are two APIs provided by Solder that should be familiar to application developers:

ExceptionStack contains information about the exception causes relative to the current exception cause. It is also the source of the exception types the invoked handlers are matched against. It is accessed in handlers by calling the method getExceptionStack() on the CaughtException object. Please see API docs for more information, all methods are fairly self-explanatory.

Tip

This object is mutable and can be modified before any handlers are invoked by an observer:

public void modifyStack(@Observes ExceptionStack stack) {

  ...
}
            

Modifying the ExceptionStack may be useful to remove exception types that are effectively meaningless such as EJBException, changing the exception type to something more meaningful such as cases like SQLException, or wrapping exceptions as custom application exception types.

This contains methods to help aid in determining what to do in the filter, it also allows you to completely replace the StackTraceElement if desired. The four "mark" methods deal with marking a stack trace and are used if "folding" a stack trace is desired, instead of dropping the frame. The StackFrame will allow for multiple marks to be set. The last method,getIndex(), will return the index of the StackTraceElement from the exception.





Integration of Solder's exception handling with other frameworks consists of one main step, and two other optional (but highly encouraged) steps:

  • creating and firing an ExceptionToCatch

  • adding any default handlers and qualifiers with annotation literals (optional)

  • supporting ServiceHandlers for creating exception handlers

ServiceHandlers make for a very easy and concise way to define exception handlers. The following example comes from the jaxrs example in the distribution:

@HandlesExceptions

@ExceptionResponseService
public interface DeclarativeRestExceptionHandlers
{
               
   @SendHttpResponse(status = 403, message = "Access to resource denied (Annotation-configured response)")
   void onNoAccess(@Handles @RestRequest CaughtException<AccessControlException> e);
   @SendHttpResponse(status = 400, message = "Invalid identifier (Annotation-configured response)")
   void onInvalidIdentifier(@Handles @RestRequest CaughtException<IllegalArgumentException> e);
}
      

All the vital information that would normally be done in the handler method is actually contained in the @SendHttpResponse annotation. The only thing left is some boiler plate code to setup the Response. In a jax-rs application (or even in any web application) this approach helps developers cut down on the amount of boiler plate code they have to write in their own handlers and should be implemented in any Solder integration, however, there may be situations where ServiceHandlers simply do not make sense.

Note

If ServiceHandlers are implemented make sure to document if any of the methods are called from CaughtException, specifically abort(), handled() or rethrow(). These methods affect invocation of other handlers (or rethrowing the exception in the case of rethrow()).

You're now ready to dive into the Servlet enhancements provided for you by Solder!

By including the Solder module in your web application (and performing the necessary listener configuration for pre-Servlet 3.0 environments), the servlet lifecycle events will be propagated to the CDI event bus so you can observe them using observer methods on CDI beans. Solder also fires additional lifecycle events not offered by the Servlet API, such as when the response is initialized and destroyed.

This category of events corresponds to the event receivers on the javax.servlet.ServletContextListener interface. The event propagated is a javax.servlet.ServletContext (not a javax.servlet.ServletContextEvent, since the ServletContext is the only relevant information this event provides).

There are two qualifiers provided in the org.jboss.solder.servlet.event package (@Initialized and @Destroyed) that can be used to observe a specific lifecycle phase of the servlet context.

The servlet context lifecycle events are documented in the table below.

QualifierTypeDescription
@Default (optional)javax.servlet.ServletContextThe servlet context is initialized or destroyed
@Initializedjavax.servlet.ServletContextThe servlet context is initialized
@Destroyedjavax.servlet.ServletContextThe servlet context is destroyed

If you want to listen to both lifecycle events, leave out the qualifiers on the observer method:

public void observeServletContext(@Observes ServletContext ctx) {

    System.out.println(ctx.getServletContextName() + " initialized or destroyed");
}

If you are interested in only a particular lifecycle phase, use one of the provided qualifers:

public void observeServletContextInitialized(@Observes @Initialized ServletContext ctx) {

    System.out.println(ctx.getServletContextName() + " initialized");
}

As with all CDI observers, the name of the method is insignificant.

These events are fired using a built-in servlet context listener. The CDI environment will be active when these events are fired (including when Weld is used in a Servlet container). The listener is configured to come before listeners in other extensions, so the initialized event is fired before other servlet context listeners are notified and the destroyed event is fired after other servlet context listeners are notified. However, this order cannot be not guaranteed if another extension library is also configured to be ordered before others.

The servlet context initialized event described in the previous section provides an ideal opportunity to perform startup logic as an alterative to using an EJB 3.1 startup singleton. Even better, you can configure the bean to be destroyed immediately following the initialization routine by leaving it as dependent scoped (dependent-scoped observers only live for the duration of the observe method invocation).

Here's an example of entering seed data into the database in a development environment (as indicated by a stereotype annotation named @Development).

@Stateless

@Development
public class SeedDataImporter {
    @PersistenceContext
    private EntityManager em;
    public void loadData(@Observes @Initialized ServletContext ctx) {
        em.persist(new Product(1, "Black Hole", 100.0));
    }
}

If you'd rather not tie yourself to the Servlet API, you can observe the org.jboss.solder.servlet.WebApplication rather than the ServletContext. WebApplication is a informational object provided by Solder that holds select information about the ServletContext such as the application name, context path, server info and start time.

The web application lifecycle events are documented in the table below.

QualifierTypeDescription
@Default (optional)WebApplicationThe web application is initialized, started or destroyed
@InitializedWebApplicationThe web application is initialized
@StartedWebApplicationThe web application is started (ready)
@DestroyedWebApplicationThe web application is destroyed

Here's the equivalent of receiving the servlet context initialized event without coupling to the Servlet API:

public void loadData(@Observes @Initialized WebApplication webapp) {

    System.out.println(webapp.getName() + " initialized at " + new Date(webapp.getStartTime()));
}

If you want to perform initialization as late as possible, after all other initialization of the application is complete, you can observe the WebApplication event qualified with @Started.

public void onStartup(@Observes @Started WebApplication webapp) {

    System.out.println("Application at " + webapp.getContextPath() + " ready to handle requests");
}

The @Started event is fired in the init method of a built-in Servlet with a load-on-startup value of 99999.

You can also use WebApplication with the @Destroyed qualifier to be notified when the web application is stopped. This event is fired by the aforementioned built-in Servlet during it's destroy method, so likely it should fire when the application is first released.

public void onShutdown(@Observes @Destroyed WebApplication webapp) {

    System.out.println("Application at " + webapp.getContextPath() + " no longer handling requests");
}

This category of events corresponds to the event receivers on the javax.servlet.ServletRequestListener interface. The event propagated is a javax.servlet.ServletRequest (not a javax.servlet.ServletRequestEvent, since the ServletRequest is the only relevant information this event provides).

There are two qualifiers provided in the org.jboss.solder.servlet.event package (@Initialized and @Destroyed) that can be used to observe a specific lifecycle phase of the servlet request and a secondary qualifier to filter events by servlet path (@Path).

The servlet request lifecycle events are documented in the table below.

QualifierTypeDescription
@Default (optional)javax.servlet.ServletRequestA servlet request is initialized or destroyed
@Initializedjavax.servlet.ServletRequestA servlet request is initialized
@Destroyedjavax.servlet.ServletRequestA servlet request is destroyed
@Default (optional)javax.servlet.http.HttpServletRequestAn HTTP servlet request is initialized or destroyed
@Initializedjavax.servlet.http.HttpServletRequestAn HTTP servlet request is initialized
@Destroyedjavax.servlet.http.HttpServletRequestAn HTTP servlet request is destroyed
@Path(PATH)javax.servlet.http.HttpServletRequestSelects HTTP request with servlet path matching PATH (drop leading slash)

If you want to listen to both lifecycle events, leave out the qualifiers on the observer:

public void observeRequest(@Observes ServletRequest request) {

    // Do something with the servlet "request" object
}

If you are interested in only a particular lifecycle phase, use a qualifer:

public void observeRequestInitialized(@Observes @Initialized ServletRequest request) {

    // Do something with the servlet "request" object upon initialization
}

You can also listen specifically for a javax.servlet.http.HttpServletRequest simply by changing the expected event type.

public void observeRequestInitialized(@Observes @Initialized HttpServletRequest request) {

    // Do something with the HTTP servlet "request" object upon initialization
}

You can associate an observer with a particular servlet request path (exact match, no leading slash).

public void observeRequestInitialized(@Observes @Initialized @Path("offer") HttpServletRequest request) {

    // Do something with the HTTP servlet "request" object upon initialization
    // only when servlet path /offer is requested
}

As with all CDI observers, the name of the method is insignificant.

These events are fired using a built-in servlet request listener. The listener is configured to come before listeners in other extensions, so the initialized event is fired before other servlet request listeners are notified and the destroyed event is fired after other servlet request listeners are notified. However, this order cannot be not guaranteed if another extension library is also configured to be ordered before others.

The Servlet API does not provide a listener for accessing the lifecycle of a response. Therefore, Solder simulates a response lifecycle listener using CDI events. The event object fired is a javax.servlet.ServletResponse.

There are two qualifiers provided in the org.jboss.solder.servlet.event package (@Initialized and @Destroyed) that can be used to observe a specific lifecycle phase of the servlet response and a secondary qualifier to filter events by servlet path (@Path).

The servlet response lifecycle events are documented in the table below.

QualifierTypeDescription
@Default (optional)javax.servlet.ServletResponseA servlet response is initialized or destroyed
@Initializedjavax.servlet.ServletResponseA servlet response is initialized
@Destroyedjavax.servlet.ServletResponseA servlet response is destroyed
@Default (optional)javax.servlet.http.HttpServletResponseAn HTTP servlet response is initialized or destroyed
@Initializedjavax.servlet.http.HttpServletResponseAn HTTP servlet response is initialized
@Destroyedjavax.servlet.http.HttpServletResponseAn HTTP servlet response is destroyed
@Path(PATH)javax.servlet.http.HttpServletResponseSelects HTTP response with servlet path matching PATH (drop leading slash)

If you want to listen to both lifecycle events, leave out the qualifiers.

public void observeResponse(@Observes ServletResponse response) {

    // Do something with the servlet "response" object
}

If you are interested in only a particular one, use a qualifer

public void observeResponseInitialized(@Observes @Initialized ServletResponse response) {

    // Do something with the servlet "response" object upon initialization
}

You can also listen specifically for a javax.servlet.http.HttpServletResponse simply by changing the expected event type.

public void observeResponseInitialized(@Observes @Initialized HttpServletResponse response) {

    // Do something with the HTTP servlet "response" object upon initialization
}

If you need access to the ServletRequest and/or the ServletContext objects at the same time, you can simply add them as parameters to the observer methods. For instance, let's assume you want to manually set the character encoding of the request and response.

public void setupEncoding(@Observes @Initialized ServletResponse res, ServletRequest req) throws Exception {

    if (this.override || req.getCharacterEncoding() == null) {
        req.setCharacterEncoding(encoding);
        if (override) {
            res.setCharacterEncoding(encoding);
        }
    }
}

As with all CDI observers, the name of the method is insignificant.

Rather than having to observe the request and response as separate events, or include the request object as an parameter on a response observer, it would be convenient to be able to observe them as a pair. That's why Solder fires an synthetic lifecycle event for the wrapper type ServletRequestContext. The ServletRequestContext holds the ServletRequest and the ServletResponse objects, and also provides access to the ServletContext.

There are two qualifiers provided in the org.jboss.solder.servlet.event package (@Initialized and @Destroyed) that can be used to observe a specific lifecycle phase of the servlet request context and a secondary qualifier to filter events by servlet path (@Path).

The servlet request context lifecycle events are documented in the table below.

QualifierTypeDescription
@Default (optional)ServletRequestContextA request is initialized or destroyed
@InitializedServletRequestContextA request is initialized
@DestroyedServletRequestContextA request is destroyed
@Default (optional)HttpServletRequestContextAn HTTP request is initialized or destroyed
@InitializedHttpServletRequestContextAn HTTP request is initialized
@DestroyedHttpServletRequestContextAn HTTP request is destroyed
@Path(PATH)HttpServletRequestContextSelects HTTP request with servlet path matching PATH (drop leading slash)

Let's revisit the character encoding observer and examine how it can be simplified by this event:

public void setupEncoding(@Observes @Initialized ServletRequestContext ctx) throws Exception {

    if (this.override || ctx.getRequest().getCharacterEncoding() == null) {
        ctx.getRequest().setCharacterEncoding(encoding);
        if (override) {
            ctx.getResponse().setCharacterEncoding(encoding);
        }
    }
}

You can also observe the HttpServletRequestContext to be notified only on HTTP requests.

Since observers that have access to the response can commit it, an HttpServletRequestContext observer that receives the initialized event can effectively work as a filter or even a Servlet. Let's consider a primitive welcome page filter that redirects visitors to the start page:

public void redirectToStartPage(@Observes @Path("") @Initialized HttpServletRequestContext ctx)

        throws Exception {
    String startPage = ctx.getResponse().encodeRedirectURL(ctx.getContextPath() + "/start.jsf");
    ctx.getResponse().sendRedirect(startPage);
}

Now you never have to write a Servlet listener, Servlet or Filter again!

Solder provides producers that expose a wide-range of information available in a Servlet environment (e.g., implicit objects such as ServletContext and HttpSession and state such as HTTP request parameters) as beans. You access this information by injecting the beans produced. This chapter documents the Servlet objects and request state that Solder exposes and how to inject them.

The @RequestParam qualifier allows you to inject an HTTP request parameter (i.e., URI query string or URL form encoded parameter).

Assume a request URL of /book.jsp?id=1.

@Inject @RequestParam("id")

private String bookId;

The value of the specified request parameter is retrieved using the method ServletRequest.getParameter(String). It is then produced as a dependent-scoped bean of type String qualified @RequestParam.

The name of the request parameter to lookup is either the value of the @RequestParam annotation or, if the annotation value is empty, the name of the injection point (e.g., the field name).

Here's the example from above modified so that the request parameter name is implied from the field name:

@Inject @RequestParam

private String id;

If the request parameter is not present, and the injection point is annotated with @DefaultValue, the value of the @DefaultValue annotation is returned instead.

Here's an example that provides a fall-back value:

@Inject @RequestParam @DefaultValue("25")

private String pageSize;

If the request parameter is not present, and the @DefaultValue annotation is not present, a null value is injected.

Solder provides a simple, yet robust foundation for modules and/or applications to establish a customized exception handling process. Solder's Servlet integration ties into the exception handling model by forwarding all unhandled Servlet exceptions to the exception handling framework so that they can be handled in a centralized, extensible and uniform manner.

Typically, the BeanManager is obtained using some form of injection. However, there are scenarios where the code being executed is outside of a managed bean environment and you need a way in. In these cases, it's necessary to lookup the BeanManager from a well-known location.

The standard mechanism for locating the BeanManager from outside a managed bean environment, as defined by the JSR-299 specification, is to look it up in JNDI. However, JNDI isn't the most convenient technology to depend on when you consider all popular deployment environments (think Tomcat and Jetty).

As a simpler alternative, Solder binds the BeanManager to the following servlet context attribute (whose name is equivalent to the fully-qualified class name of the BeanManager interface:

javax.enterprise.inject.spi.BeanManager

Solder also includes a provider that retrieves the BeanManager from this location. Anytime the Solder module needs a reference to the BeanManager, it uses this lookup mechanism to ensure that the module works consistently across deployment environments, especially in Servlet containers.

You can retrieve the BeanManager in the same way. If you want to hide the lookup, you can extend the BeanManagerAware class and retrieve the BeanManager from the the method getBeanManager(), as shown here:



public class NonManagedClass extends BeanManagerAware {
    public void fireEvent() {
        getBeanManager().fireEvent("Send me to a managed bean");
    }
}
    

Alternatively, you can retrieve the BeanManager from the method getBeanManager() on the BeanManagerLocator class, as shown here:



public class NonManagedClass {
    public void fireEvent() {
        new BeanManagerLocator().getBeanManager().fireEvent("Send me to a managed bean");
    }
}
    

Under the covers, these classes look for the BeanManager in the servlet context attribute covered in this section, amongst other available strategies. Refer to the BeanManager provider chapter of the Seam Solder reference guide for information on how to leverage the servlet context attribute provider to access the BeanManager from outside the CDI environment.