The GlassFish server uses JSR-303 bean validation for ensuring that its configuration information is valid. When validation fails, the system generates an error message for the user that explains what is wrong with the requested configuration change.  As with all other output messages from GlassFish, these error messages must support internationalization (i18n) so that they can be translated to other languages. The bean validation standard and its reference implementation (RI) supports i18n through the use of a ValidationProperties resource bundle, as described in section 4.3 of the specification. However, within GlassFish, there are several problems with the use of the ValidationProperties resource bundle.

Problem 1: Modularization

The GlassFish configuration beans are spread across several modules in different projects, such as Grizzly, and these modules are delivered into the system in different packages that can be updated independently. The JSR-303 RI supports the use of only a single ValidationProperties resource bundle for an entire program. Getting all of the messages into a single resource bundle for all of GlassFish would be quite difficult. 

Problem 2: OSGi Class Loading

The GlassFish server uses OSGi to load classes. The JSR-303 RI expects the ValidationProperties resource bundle to be available via the thread context class loader. However, packages loaded using OSGi generally are not available via that class loader within GlassFish.  So if GlassFish were to use a single ValidationProperties resource bundle, it would need to be in the base classpath for the server, not within a module that is loaded via OSGi.  The configuration beans that use bean validation are in OSGi modules. 

Fortunately, the bean validation standard defines a MessageInterpolator interface that allows customization of how bean validation error messages are resolved. To solve these two problems, the GlassFish team has developed a custom MessageInterpolator class, java.org.jvnet.hk2.config.MessageInterpolatorImpl that is part of the HK2 subsystem. This MessageInterpolator provides the ability to define bean validation error messages that are local to the Java package where a bean validation annotation is used. However, because of limitations in the MessageInterpolator interface defined by JSR-303, it is necessary to follow some non-obvious conventions when defining the messages. 

Specifying a Message when using an Annotation

When using standard JSR-303 annotations such as @Min, @Max, and @Pattern, or custom annotations defined by the GlassFish project, custom messages within beans for GlassFish are defined as follows:

1. Make the bean implement the javax.validation.Payload interface.

2. Pass the message key and a payload to the annotation

3. Define the message for the key in the LocalStrings.properties file within the Java package containing the bean. 

Here is an example of following these steps for a sample bean, MyBean.

File MyBean.java

import javax.validation.Payload;
import javax.validation.constraints.Pattern;

public interface MyBean implements Payload {

	@Pattern(regexp="x+", message="{xkey}", payload=MyBean.class)
	public String getX();
	public void setX(String x);
}

File LocalStrings.properties

xkey=The value for x can only contain xs. 

The reason for passing the bean class in via the payload argument to the annotation is so that the custom MessageInterpolatorImpl class is able to obtain the class loader for the bean that is using the annotation, and using that class loader, it can find the LocalStrings resource bundle. 

To see real examples of this being used in GlassFish, see the Cluster.java file in the admin/config-api module, com.sun.enterprise.config.serverbeans package.

Supporting I18n in a Custom Bean Validation Annotation

When implementing a custom annotation, one of the methods that needs to be defined is the "message" method that defines a default value for the message parameter. Also, a payload method must also be defined.  Here is a sample custom annotation:

@Retention(RUNTIME)
@Target({FIELD, METHOD})
@Documented
@Constraint(validatedBy = JavaClassNameValidator.class)
public @interface JavaClassName {
    String message() default "must be a valid Java Class Name";
    Class[] groups() default {};
    Class[] payload() default {};
}

Note that the default value for the message here does not support i18n. This means that for the system to support i18n, any bean that uses this annotation must provide a message with a payload as shown in the previous section. Almost all of the custom bean validators defined within GlassFish are like this. 
Ideally, it would be possible to define the default message using a key, such as "{javaclasskey}", but to make this work, the default value of the payload would need to include a class that implements Payload, so that the MessageInterpolatorImpl could find the resource bundle containing the message.  However, the JSR-303 RI insists that the default value for payload be an empty array, so this isn't currently possible with the RI. (We are in the process of asking the RI implementers about this restriction.)

So for now, any usage of a custom bean validation annotation must provide its own error message using the technique in the previous section.