October 14th, 2007 Radim Marek
An important characteristic of every framework is a level of possible extensibility. JBoss Seam scores top marks in this perspective. Reason for it is simple. The main goal, to introduce consistent programming model that will make various frameworks to work together, is not just promoted for application developer, but also used through-out Seam itself. So with knowledge how to use component model and a quick insight behind the scene, you can easily start extending built-in components.
Mechanism you need to understand is component deployment process. Seam application start-up is triggered by the context listener which instantiates the Initialization object responsible for building configuration metadata. That is accomplished by collection of data from standard configuration files (WEB-INF/components.xml and deprecated WEB-INF/events.xml), configuration properties (supplied both from servlet context and file name seam.properties in the root of the classpath) and eventually by scanning archives in the application classpath for classes annotated with @Name which implies the Seam component.
Dependency Manager is then responsible for evaluation of component dependency to determine which components are to be installed.
And that’s the place where Seam utilizes another annotation - @Install. It does not only specify whether or not the component should be installed (this might be overridden by configuration files) and its dependencies, but also specifies precedence of the component. If two components with the same name are present, Dependency Manager will offer for installation only the one with higher precedence.
The precedence is specified integer value and following pre-defined constants are available:
- BUILT_IN [0] used for built-in components
- FRAMEWORK [10] to be used by frameworks which extend Seam
- APPLICATION [20] default precedence, to be used by application components
- DEPLOYMENT [30] used for overriding components for particular deployment
- MOCK [40] for objects used in testing
Further control over the components deployment process is provided by following two annotations:
- @BypassInterceptors which disables all Seam interceptors on particular class or method.
- @Startup to control initialization of component depending of scope its available in
With this overview you’re ready to extend any provided component, either Seam built-in or available within 3rd party framework or application.
Extending Identity object
As example I’ll show you how to extend built-in identity object to support custom JAAS Login Module that requires custom callback handler to be used to obtain/provide further information. This particular implementation adds functionality to support NTLM as other authentication method (together with customized jCIFS library) and NTLM support iself is not part of this code snapshot.
@Name("org.jboss.seam.security.identity")
@Scope(SESSION)
@Install(precedence = APPLICATION)
@BypassInterceptors
@Startup
public class NTLMIdentity extends Identity {
//
// custom code
//
@Override
public String login() {
try {
retrieveNTLMAuthenticationDetails();
} catch (LoginException le) {
log.error("Unable to retrieve NTLM authentication details!", le);
return null;
}
}
@Override
protected CallbackHandler getDefaultCallbackHandler() {
return new CallbackHandler() {
public void handle(Callback[] callbacks)
throws IOException, UnsupportedCallbackException {
for (int i = 0; i < callbacks.length; i++) {
if (callbacks[i] instanceof NameCallback) {
((NameCallback) callbacks[i]).setName(getUsername());
} else if (callbacks[i] instanceof PasswordCallback) {
((PasswordCallback) callbacks[i]).setPassword(getPassword() != null ?
getPassword().toCharArray() : null);
} else if (callbacks[i] instanceof SFSIdentityCallbackHandler) {
processSFSCallbackHandler((SFSIdentityCallbackHandler) callbacks[i]);
} else {
throw new UnsupportedCallbackException(callbacks[i], "Unsupported callback");
}
}
}
};
}
//
// custom code
//
}
Where @Name is specified to override Seam built-in Identity component, and precedence set higher than BUILT_IN. If you run application, the result of #{identity} and Identity.instance() will always be NTLMIdentity object.
Posted in seam | 5 Comments »