resin tutorial Connector

This is an advanced Connector tutorial for those interested in implementing Connector drivers or who just want to understand the inner workings. Connectors are a generalization of the JDBC DataSource, supporting pooling and transactions for any Connector following the interface.

demo
Configures the Connector User interface for the connection User interface for the connection factory Driver (SPI) interface for the connection Driver (SPI) interface for the connection factory The demo servlet for the tutorial
The driver's system of connections and factories.

The connector architecture generalizes the pooling and transaction capabilities of the JDBC DataSource, allowing other connections and even user code to take advantage of pooling and transactions. From a user's perspective, the system is simple: a factory creates connection objects. The driver implementor's view is a bit more complex, of course, but still only requires two extra classes: the server's view of the factory and the server's view of the connection.

Understanding the connector system starts from the main class, the user connection, the user's view of the connection.

user connectionUser's view of the connection user connection factoryUser's view of the connection factory managed connectionResin's view of the connection managed connection factoryResin's view of the connection factory

The user connection is the main application interface. The connector can use any API for the user connection. Typically, connections will at least have a close() method which will return the connection to the pool.

The user's connection, ConnectionImpl in the tutorial, has a similar function to the JDBC Connection class. It's the main object that applications will use. Typically, it will be used in a single threaded fashion and will be closed in a finally block to return the connection to the pool.

When the connection is returned to the pool, the user connection, ConnectionImpl is not reused.

The user connection and the user connection factory are the only interfaces user code will see. The user connection factory, like JDBC's DataSource is responsible for creating user connections.

The user connection factory creates user connections. Users will generally get the user connection factory from JNDI during initialization and use it for all requests. Each request will get a new user connection from the user connection factory. In the example, the user connection factory is ConnectionFactoryImpl.

Like the user connection, the user connection factory may have any API appropriate to the connector. For example, a JDBC connector will user the standard DataSource.

The user connection factory must be thread-safe, since multiple requests may need new connections simultaneously.

When the user requests a new user connection from the user connection factory, the user factory calls Resin's ConnectionManager interface to request a new connection. To create the new connection, Resin asks the driver's controlling class, the managed connection factory, for a new managed connection.

Each user connection uses an underlying managed connection to talk to the resource. When the user is done with the connection, the close() method puts the managed connection back into Resin's pool for later reuse. Once closed, the user connection is discarded.

The driver can choose whether most of the user connection logic is in the user connection or the managed connection. If most of the logic is in the user connection, the managed connection will typically be responsible connection resources, like a socket, but the user connection may be responsible for the protocol. Another architecture will have the user connection act as a facade to the underlying code in the managed connection. The architecture will depend on the driver's needs.

Resin's main interface to the connector is the managed connection factory. The managed connection factory is configured in the web.xml using bean-style initialization. Resin calls on the managed connection factory for both the user connection factory and to allocate new managed connections.

Using the JCA connector follows the same pattern as for JDBC. The servlet looks up the ConnectionFactory in the servlet's init() method and uses the connection in a try .. finally block.

As with JDBC, the user must put the close() in a finally block or the connection will leak.

void init() { ... ic = new InitialConnection(); _factory = (ConnectionFactoryImpl) ic.lookup("java:comp/env/factory"); } void service(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { ConnectionImpl conn = null; try { conn = _factory.getConnection(); ... } finally { if (conn != null) conn.close(); } }

The demo servlet just prints the ConnectionFactory and the ConnectionImpl. As the following result show, each connection request uses a new ConnectionImpl, but keeps using the same ManagedConnectionImpl.

Factory: ConnectionFactoryImpl[ManagedConnectionFactoryImpl[example]] Connection: ConnectionImpl[example-12-conn,ManagedConnectionImpl[example-0]]

As usual, Resin uses bean-style configuration for the connector. The example ManagedConnectionFactoryImpl has a setName configuration method. The <init> tag in the configuration file sets that value. If needed, you can use JSP EL expressions to assign the name value.

The attribute is the JNDI name for the user connection factor, in this case "java:comp/env/factory". The <type> tag is the class name of the factory and the <init> section initializes the factory.

<resource name="factory"> <type>example.ManagedConnectionFactoryImpl</type> <init> <name>example</name> </init> </resource>

Since looking at the implementation methods can be a bit confusing, it's best to approach them as a set of method call chains.

During configuration, Resin needs to get the UserConnectionFactory from the ManagedConnectionFactory and store it in JNDI.

  1. Resin instantiates and configures the ManagedConnectionFactory from the configuration file.
  2. Resin calls ManagedConnectionFactory.createConnectionFactory for the user connection factory. It passes along a ConnectionManager which points back to Resin.
  3. Resin stores the user connection factory in JNDI.

The ConnectionManager will be important in the next step to get a connection.

From the user's perspective, the main point of the connector is getting a connection. The user asks the user connection factory for the request.

  1. The user calls the user connection factory's getConnection method. This method would be specific to the user connection factory's API.
  2. The user connection factory calls Resin's ConnectionManager.allocateConnection method to ask for a new connection.
  3. Resin checks the pool but it's empty, so needs to get a new connection.
  4. Resin calls the managed connection factory's createManagedConnection method to get a new managed connection.
  5. Resin registers itself as a listener for the ManagedConnection, so it will know when the connections close or have a fatal error.
  6. Resin calls the managed connection's getConnection method to get a new user connection.
  7. Resin finally returns the user connection to the user.

After the user is done with the connection, the finally block will close the connection. The connector takes the following steps to put the connection into the pool:

  1. The user calls the connection's close method. This method is specific to each API, but close is a good choice.
  2. The connection notifies the listeners in the managed connection that the connection has closed. In the example, ConnectionImpl calls the ManagedConnectionImpl.close() method.
  3. Since Resin is registered as a listener, the managed connection calls Resin's connectionClosed method.
  4. Resin returns the managed connection to the pool and marks it idle, ready for the next request.

From the user's perspective this looks exactly like getting a new connection does. There's a bit more work for the managed connection factory, though.

  1. The user calls the user connection factory's getConnection method. This method would be specific to the user connection factory's API.
  2. The user connection factory calls Resin's ConnectionManager.allocateConnection method to ask for a new connection.
  3. Resin checks the pool and there are idle connections. But Resin needs to ask the managed connection factory if any can be used for this request.
  4. Resin calls the managed connection factory's matchManagedConnections with the idle managed connection pool to see if any can be used.
  5. If there's a valid managed connection in the pool, the managed connection factory return it.
  6. Resin then takes the managed connection from the pool.
  7. Resin calls the managed connection's getConnection method to get a new user connection.
  8. Resin returns the user connection to the user.

Since this example uses the JCA API, the code is compatible across all application servers. The configuration, of course, will be different for each. Many other applications will require generation of a .rar file and some additional XML files, although those should be relatively straightforward.