6/25/2006

Don't Overload EJB 3 Lifecycle and Interceptor Methods: Part 3

In part 1 and part 2, I wrote about the error from overloading EJB 3 lifecycle and interceptor methods, and how to detect it. You may be wondering why there is such a restriction.

I don't have a clear answer. I guess the main purpose is to make it easier to implement this feature in EJB 3 containers. With this restriction, a container only needs to search (e.g., using reflection) for lifecycle and interceptor methods by their name, ignoring parameters. Processing method parameters may affect performance, though I'm not sure to what extent.

Variable arguments (varargs) introduced in JDK 5 adds more complexity to method parameter processing. I have yet to see any formal support of varargs in JavaEE technologies. So it seems convenient to just leave method parameters out.

Lifecycle and interceptor methods also need to be expressed in XML descriptor files, though it's less common in JavaEE 5. It's pretty verbose to uniquely represent a java method in XML format, and usually takes this form:

<method>
<class>com.javahowto.test.MyInterceptor</class>
<method-name>init</method-name>
<method-params>
<method-param>java.lang.String</method-param>
<method-param>int[]</method-param>
</method-params>
</method>
If we don't need to worry about all these method parameters, it will certainly make XML descriptors less clumsy.

So this is how a lifecycle method is defined in javaee_5.xsd:
<xsd:complexType name="lifecycle-callbackType">
<xsd:annotation>
<xsd:documentation>
The lifecycle-callback type specifies a method on a
class to be called when a lifecycle event occurs.
Note that each class may have only one lifecycle
callback method for any given event
and that the method may not be overloaded.

If the lifefycle-callback-class element is missing then
the class defining the callback is assumed to be the
component class in scope at the place in the descriptor
in which the callback definition appears.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="lifecycle-callback-class"
type="javaee:fully-qualified-classType"
minOccurs="0"/>
<xsd:element name="lifecycle-callback-method"
type="javaee:java-identifierType"/>
</xsd:sequence>
</xsd:complexType>
And this is how JavaEE interceptor method is defined in ejb-jar_3_0.xsd:
<xsd:complexType name="around-invokeType">
<xsd:annotation>
<xsd:documentation>
The around-invoke type specifies a method on a
class to be called during the around invoke portion of an
ejb invocation. Note that each class may have only one
around invoke method and that the method may not be
overloaded.

If the class element is missing then
the class defining the callback is assumed to be the
interceptor class or component class in scope at the
location in the descriptor in which the around invoke
definition appears.
</xsd:documentation>
</xsd:annotation>
<xsd:sequence>
<xsd:element name="class"
type="javaee:fully-qualified-classType"
minOccurs="0"/>
<xsd:element name="method-name"
type="javaee:java-identifierType"/>
</xsd:sequence>
</xsd:complexType>
How about setter injection methods? Is it OK to overload JavaEE setter injection methods? There seems to be no clear specification, but it's reasonable to expect the same restriction to apply to setter injection methods. In XML descriptor, the corresponding element for a setter injection method is java-identifierType, as declared in javaee_6.xsd:
  <xsd:complexType name="java-identifierType">
<xsd:annotation>
<xsd:documentation>

The java-identifierType defines a Java identifier.
The users of this type should further verify that
the content does not contain Java reserved keywords.

</xsd:documentation>
</xsd:annotation>
<xsd:simpleContent>
<xsd:restriction base="javaee:string">
<xsd:pattern value="($|_|\p{L})(\p{L}|\p{Nd}|_|$)*"/>
</xsd:restriction>
</xsd:simpleContent>
</xsd:complexType>
There is no way for java-identifierType to distinguish between overloaded methods. I take it as another reason why setter injection methods should not be overloaded. For instance, the following is invalid:
private User user;

@EJB
public void setUser(User u) {
this.user = u;
}

// AVOID THIS
public void setUser(String uid) {
this.user = getUser(uid);
}