Java Resource Injection

{ Posted on 11:31 AM by Ralph Tavarez }
If you are creating an API it may be necessary to provide resources to the API client. Many APIs use XML configuration files, resource files, or additional helper classes / methods to provide access to resources. All of which increase the learning curve required to utilize your code. Ideally your API should simplify resource provisioning and consumption. My personal preference is to follow the current trend towards annotations as an API convention.

In this post I will cover the convention of resource utilization via annotation and I will cover how to inject the resource by utilizing Java reflection. To clarify the convention, the client needs to annotate one or more fields and the fields must be of the proper Java type. If the client meets the conditions, the API container will automatically assign an appropriate resource to the client's field member even of the field is not public.

To demonstrate how this is done I will create a simple client:

package sample;

import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.NamingException;

public class InjectionClient {
//field to be injected with required jndi name property
@ResourceProvider(jndiName="jdbc/MyPool")
private ConnectionProvider provider;

public void foo(String sql)
throws NamingException, SQLException {
Connection con = null;

try {
// provider already set by container
con = provider.getConnection();

// ... do something useful
} finally {
try {
if (con != null) con.close();
} catch (SQLException ex) {}
}
}
}


Notice how simple and clean the code is for the client to consume the resource. Simply define a member of the correct type and annotate accordingly. Next lets look at the annotation being used by the client:

package sample;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// Annotation to be used for field declarations
@Target(ElementType.FIELD)
// Record annotation in the class file and make it available at run time
@Retention(RetentionPolicy.RUNTIME)
public @interface ResourceProvider {
String jndiName();
}



In this case the annotation tells the client that a jndi name is require. It also tells the client that the annotation is used for field declarations which will be recorded in the class file and will be accessible during runtime. The nice part of the annotation as part of an API is that most Java IDE's will help validate the proper usage of the annotation.

Finally lets take a look at the injection code:

package sample;

import java.lang.reflect.Field;

public class Injector {
public static void injectConnectionResource(Object o)
throws IllegalArgumentException, IllegalAccessException {
ResourceProvider a;

// iterate threw the fields
for (Field field : o.getClass().getDeclaredFields()) {
// set accessibility just in case the field is private
field.setAccessible(true);

// get the required annotation
a = field.getAnnotation(ResourceProvider.class);

// validate type and annotation
if (a != null && field.getType().isAssignableFrom(ConnectionProvider.class)) {
// inject
field.set(o, new ConnectionProvider(a.jndiName()));
}
}
}
}



To complete the example below is the code for the actual resource provider:

package sample;

import java.sql.Connection;
import java.sql.SQLException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sql.DataSource;

public class ConnectionProvider {
private String jndiName;

public ConnectionProvider(String jndiName) {
this.jndiName = jndiName;
}

public Connection getConnection() throws NamingException, SQLException {
InitialContext context = new InitialContext();
DataSource dataSource = (DataSource) context.lookup(jndiName);
Connection con = dataSource.getConnection();

return con;
}
}


There are 2 gotchas with this approach:

1)If the field is set to anything other than public you may have an issue. Refection allows you to suppress Java language access checking but this only works if the no security manager is present. If a security manager is present then the “suppressAccessChecks” reflection permission must not be flagged as true.

2)Reflection is slow, painfully slow.

Gotcha's aside, resource injection via reflection based on annotation convention is a great approach to simplify an API.

No Response to "Java Resource Injection"

Post a Comment