Java EE CDI Disposer methods example
Introduction
CDI Producer methods may be used to create resources that are consumed by an application in a very specific context. These resources may need to be cleaned up by the container when the application doesn't need them any more. This clean up process is provided by CDI in the form of Disposer methods as we will see in this tutorial.
This tutorial considers the following environment:
- Ubuntu 12.04
- JDK 1.7.0.21
- Weld 1.1.10
- Tomcat 7.0.35
Weld is the CDI reference implementation
A simple resource
In this tutorial we will be producing and disposing a simple resource that is represented by a Connection. Following next is the Connection interface and implementation that should be very straightforward:
package com.byteslounge.connection; public interface Connection { void connect(); void closeConnection(); }
package com.byteslounge.connection; public class ConnectionImpl implements Connection { @Override public void connect() { System.out.println("Connecting..."); } @Override public void closeConnection() { System.out.println("Closing connection..."); } }
The Producer and the Disposer
Now we define a class that will contain both the Connection producer and the disposer:
package com.byteslounge.connection; import javax.enterprise.context.RequestScoped; import javax.enterprise.inject.Produces; import javax.enterprise.inject.Disposes; public class ConnectionFactory { @Produces @RequestScoped @TestConnection public Connection getConnection(){ Connection conn = new ConnectionImpl(); conn.connect(); return conn; } public void closeConnection( @Disposes @TestConnection Connection connection){ connection.closeConnection(); } }
The method getConnection() is the producer method as we have seen in the previous tutorial so we will not describe it in detail here (see Java EE CDI Producer methods tutorial).
The new element here is the disposer method: closeConnection(). A disposer method must be matched against a producer method, ie. it must have a parameter annotated with @Disposes which type matches a producer method return type (in this case Connection type). This parameter must also have the same qualifiers as the producer method (only if any qualifier was used), in this case the @TestConnection qualifier that we will see next.
A disposer method may also have additional parameters which the container will try to inject.
Our producer method is annotated with @RequestScoped. This means that for a single HTTP request that injects a Connection instance, the producer method will be called once and the Connection reference will be maintained by the container during the life time of that HTTP request, and consequently will be always used in further Connection injection points.
When the HTTP request finishes the container will call the disposer method in order to perform the resource clean up.
Why do we need the @TestConnection qualifier? If we did not used the qualifier the container would not know how to inject a Connection because it had to available options: one is to inject a Connection instance by the means of the producer method while the other is to instantiate a ConnectionImpl instance and to inject it directly into the injection point. What we did was to use a qualifier so we can disambiguate between both the of these scenarios.
The qualifier follows next:
package com.byteslounge.connection; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.ElementType.METHOD; import static java.lang.annotation.ElementType.PARAMETER; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.inject.Qualifier; @Qualifier @Retention(RUNTIME) @Target({FIELD, TYPE, METHOD, PARAMETER}) public @interface TestConnection { }
Finally we inject the connection like the following:
@Inject @TestConnection private Connection connection;
Testing
In order to test the example we may use a simple servlet:
package com.byteslounge.servlet; import java.io.IOException; import javax.inject.Inject; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.byteslounge.connection.Connection; import com.byteslounge.connection.TestConnection; @WebServlet(name = "testServlet", urlPatterns = {"/testServlet"}) public class TestConnectionServlet extends HttpServlet { private static final long serialVersionUID = -3995970242890631574L; @Inject @TestConnection private Connection connection; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("Doing something with connection: " + connection.toString()); } }
When we access this servlet the following output will be generated:
Doing something with connection: ...ConnectionImpl@3dcc0a0f
Closing connection...
The tutorial source code is available for download at the end of this page and it's configured to be run on Tomcat.