| Recent Entries |
| Seam News | (206) |
| Seam | (130) |
| Hibernate | (113) |
| RichFaces | (92) |
| Contexts and Dependency Injection | (87) |
| Web Beans | (61) |
| JBoss Tools | (60) |
| News | (57) |
| Eclipse | (56) |
| JavaServer Faces | (55) |
| Ceylon | (46) |
| Core Release | (44) |
| JBoss Tools Eclipse | (44) |
| Weld | (43) |
| Hibernate Search | (41) |
In my previous little rant, I showed you how to use @Alternative and alternative stereotypes to easily change bean implementations based upon deployment scenario. But that's not the end of the story. There's two other things I would sometimes like to be able to change at deployment time:
- interceptors
- decorators
Let's consider security. Suppose I have an interceptor that implements method-level role-based security for beans in the business layer
.
@Secure @Interceptor
class SecurityInterceptor {
@Inject User user;
@AroundInvoke
Object checkRole(InvocationContext ctx) throws Exception {
String[] roles = ctx.getMethod().getAnnotation(Secure.class).value();
for (String role: role) {
if ( !user.getRoles().contains(role) ) throw new AuthorizationException(role);
}
return ctx.proceed();
}
}
I also have a separate data access layer, consisting of beans that implement the following interface:
public interface DataAccess<T, V> {
public V getId(T object);
public T load(V id);
public void save(T object);
public void delete(T object);
public Class<T> getDataType();
...
}
And I have a decorator that adds row-level security to my data access layer.
@Decorator
class DataSecurityDecorator<T, V> implements DataAccess<T, V> {
@Inject @Delegate DataAccess<T, V> delegate;
@Inject User user;
public void save(T object) {
authorize(SecureAction.SAVE, object);
delegate.save(object);
}
public void delete(T object) {
authorize(SecureAction.DELETE, object);
delegate.delete(object);
}
private void authorize(SecureAction action, T object) {
V id = delegate.getId(object);
Class<T> type = delegate.getDataType();
if ( !user.getPermissions().contains( new Permission(action, type, id) ) ) {
throw new AuthorizationException(action);
}
}
}
Well, that's all very well, but in my test environment I don't want these interceptors and decorators to be enabled. And that's why, by default, they aren't.
We need to expilicity enable the interceptor and decorator for all deployments that require security access controls by declaring them in beans.xml.
<beans>
<interceptors>
<class>org.mycompany.security.SecurityInterceptor</class>
</interceptors>
<decorators>
<class>org.mycompany.security.DataSecurityDecorator</class>
</decorators>
</beans>
Suppose we have an external resource, a database, let's say, that we want to be able to change depending upon the deployment environment. In CDI, we declare resources using a producer field declaration.
class CustomerDatabase {
static
@Resource(lookup="java:global/env/jdbc/CustomerDatasource")
@Produces @CustomerDatabase
Datasource customerDatabase;
}
The purpose of this field declaration is to associate the bean type Datasource and binding type @CustomerDatabase with the Java EE resource with the global JNDI name java:global/env/jdbc/CustomerDatasource.
Now we can easily inject our Datasource into any bean that needs it:
public class Bean {
private Datasource customerDatabase;
@Inject public Bean(@CustomerDatabase Datasource ds) {
customerDatabase = ds;
}
...
}
Now, this injected resource reference is already extremely configurable. The JNDI name is just a logical name. The actual datasource it points to can easily change depending upon the deployment. We certainly haven't hardcoded a JDBC URL, username and password! But let's say that's not enough. CDI lets you define alternative implementations of a bean.
class TestCustomerDatabase {
static
@Resource(lookup="java:global/env/jdbc/TestDatasource")
@Produces @Alternative @CustomerDatabase
Datasource testCustomerDatabase;
}
@Alternative @CustomerDatabase
class MockCustomerDatabase implements Datasource {
//operations of Datasource go here!
...
}
These classes both provide an alternative implementation of @CustomerDatabase Datasource. By default, CDI will ignore these beans, and keep using the original implementation, since it is the only implementation not declared @Alternative.
However, if we declare one of the alternatives in beans.xml, CDI will use that implementation instead:
<beans>
<alternatives>
<class>org.mycompany.resources.TestCustomerDatabase</class>
</alternatives>
</beans>
That's great, and straightforward, when we only have one or two beans that vary depending upon the deployment scenario. Well, I usually consider that to be the case in most applications. However, developers whose brains have been turned to mush by overexposure to the Spring framework think that they have hundreds of beans which might vary depending upon the deployment. That's how they justify writing great gobs of XML to explicitly list out all the classes in their application. We certainly don't want to follow them down that path!
Therefore, CDI provides the ability to define an annotation that represents a deployment scenario. These annotations are called alternative stereotypes. For example:
@Alternative @Stereotype
@Target(TYPE) @Retention(RUNTIME)
public @interface Mock {}
@Alternative @Stereotype
@Target({TYPE, METHOD, FIELD}) @Retention(RUNTIME)
public @interface Test {}
We can now use these annotations to indicate which of our beans belong to which deployment scenario:
class TestCustomerDatabase {
static
@Resource(lookup="java:global/env/jdbc/TestDatasource")
@Produces @Test @CustomerDatabase
Datasource testCustomerDatabase;
}
@Mock @CustomerDatabase
class MockCustomerDatabase implements Datasource {
//operations of Datasource go here!
...
}
Now, with just a single line of XML, we can collectively enable all @Test beans, or all @Mock beans.
<beans>
<alternatives>
<stereotype>org.mycompany.deployment.Test</stereotype>
</alternatives>
</beans>
Don't you think that's a better way?
I'm trying really hard to emphasize to the community that CDI and Weld are not just a dependency injection solution
. We did not come at this from the point of view of trying to solve dependency injection
, or of trying to build a better Spring
.
What I was really thinking about when I started working on Seam was state management in event driven architectures. How can you ensure that an event is processed with the correct set of state for the context(s) it carries with it? I wanted to get away from the awful front controller
pattern, where some big stupid stateless method is responsible for coordinating everything involved in processing the event, from state retrieval and storage, to service location, to orchestration.
There were a couple of frameworks which were ahead of their time in this respect. The one that got me started was XWork, a dependency injection solution that was part of earlier versions of WebWork. That's why I was kind of excited when I saw the same ideas in JSF (setting aside many other problems with JSF 1.0).
It's also why I pushed back so hard when some people questioned the existence of the event notification facility in CDI. Though it appeared to some folks to be a kind of cute feature stuck on the side of a dependency injection container, that's not what it was to me at all. Indeed, the idea of event processing was right at the core of what we were trying to achieve in JSR-299.
CDI (JSR-299) and Weld 1.0 are almost a reality. We've got word from Sun that CDI and the rest of Java EE 6 will be submitted to the JCP on November 9. I've spent the last few days filling out the Javadoc for the CDI API and SPI packages and making some last-minute cleanups to the spec. Meanwhile, Pete and the others are fixing bugs in the RI and TCK. This process has taken more than 3 years, and an incredible amount of pain, but we're now looking at one of the most well-reviewed JCP specifications ever.
Steven Boscarine has written a nice short tutorial on getting up and running with Weld and JSF on Jetty, using Maven. Note that there's a bit more boilerplate here than there would be in a Java EE environment, since integration of Weld in Jetty requires explicit configuration. Nevertheless, I think Steven does a good job of demonstrating that it's easy to get up and running with this stuff.
UPDATE: Steven has extended the tutorial to cover deployment to Tomcat.
|
|
|
Showing 6 to 10 of 58 blog entries tagged 'Web Beans' |
|
|