Monday, December 16, 2013

Yadic - Dependency Injection Framework from Google Code


Overview:
This article looks at Yadic, yet another Google Code framework for dependency injection. Yadic is a lightweight framework that promotes keeping implementation simple and yields lightning fast performance.

Here, we will look at a simple example how you could leverage the Yadic as your DI framework. Since the framework is so simple, most of the content in this article are self-explanatory.

You can learn more about the framework here.

A comprehensive sample code for this article can be found here.

Project Structure:

 ├── pom.xml  
 └── src  
   ├── main  
   │   └── java  
   │     └── org  
   │       └── fazlan  
   │         └── yadic  
   │           ├── common  
   │           │   ├── AbstractValueObject.java  
   │           │   └── ValueObject.java  
   │           ├── ds  
   │           │   ├── DataSourceConfig.java  
   │           │   ├── DataSourceDialect.java  
   │           │   ├── DataSourcePassword.java  
   │           │   ├── DataSourceUri.java  
   │           │   └── DataSourceUserName.java  
   │           └── persistence  
   │             ├── H2Connection.java  
   │             ├── JdbcConnectionActivator.java  
   │             ├── JdbcConnectionFactory.java  
   │             └── SqlDialect.java  
   └── test  
     └── java  
       └── org  
         └── fazlan  
           └── yadic  
             ├── ds  
             │   └── DataSourceConfigTest.java  
             ├── persistence  
             │   ├── JdbcConnectionActivatorTest.java  
             │   └── JdbcConnectionFactoryTest.java  
             └── util  
               └── DataSourceObjectMother.java  

Step 1: Creating the Project

 mvn archetype:generate -DartifactId=org.fazlan.yadic -DgroupId=org.fazlan.yadic -Dversion=1.0-SNAPSHOT -DinteractiveMode=false  

Step 2: Updating the Maven dependencies
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">  
   <modelVersion>4.0.0</modelVersion>  
   <groupId>org.fazlan.yadic</groupId>  
   <artifactId>yadic</artifactId>  
   <version>1.0-SNAPSHOT</version>  
   <packaging>jar</packaging>  
   <name>yadic</name>  
   <url>http://maven.apache.org</url>  
   <properties>  
     <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
     <testng.version>6.8</testng.version>  
     <yadic.version>168</yadic.version>  
     <hamcrest.version>1.3.RC2</hamcrest.version>  
     <totallylazy.version>1165</totallylazy.version>  
   </properties>  
   <dependencies>  
     <dependency>  
       <groupId>com.h2database</groupId>  
       <artifactId>h2</artifactId>  
       <version>1.3.170</version>  
     </dependency>  
     <dependency>  
       <groupId>com.googlecode.totallylazy</groupId>  
       <artifactId>totallylazy</artifactId>  
       <version>${totallylazy.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>com.googlecode.yadic</groupId>  
       <artifactId>yadic</artifactId>  
       <version>${yadic.version}</version>  
     </dependency>  
     <dependency>  
       <groupId>org.hamcrest</groupId>  
       <artifactId>hamcrest-core</artifactId>  
       <version>${hamcrest.version}</version>  
       <scope>test</scope>  
     </dependency>  
     <dependency>  
       <groupId>org.hamcrest</groupId>  
       <artifactId>hamcrest-library</artifactId>  
       <version>${hamcrest.version}</version>  
       <scope>test</scope>  
     </dependency>  
     <dependency>  
       <groupId>org.testng</groupId>  
       <artifactId>testng</artifactId>  
       <version>${testng.version}</version>  
       <scope>test</scope>  
     </dependency>  
   </dependencies>  
 </project>  

Step 3: Creating the Container

 package org.fazlan.yadic.util;  

 import com.googlecode.yadic.Container;  
 import com.googlecode.yadic.SimpleContainer;  
 import org.fazlan.yadic.ds.DataSourceConfig;  

 public class DataSourceObjectMother {  
   . . .  

   public static Container newDIContainer() {  
     return new SimpleContainer();  
   }  
   . . .
 }  

Step 4: Adding Objects to the Container
The Framework allows several ways to add your objects into the container. Following are few ways in which you could use Yadic to manage the POJOs.

Adding as an Explicit Instance to the Container

 package org.fazlan.yadic.ds;  

 import java.util.Properties;  

 public class DataSourceConfig extends Properties {  

   public DataSourceConfig add(String key, Object value) {  
     put(key, value);  
     return this;  
   }  

   public DataSourceUri getUri() {  
     return new DataSourceUri(getProperty("datasource.uri"));  
   }  

   public DataSourcePassword getPassword() {  
     return new DataSourcePassword(getProperty("datasource.password"));  
   }  

   public DataSourceUserName getUserName() {  
     return new DataSourceUserName(getProperty("datasource.username"));  
   }  

   public DataSourceDialect getDialect() {  
     return new DataSourceDialect(getProperty("datasource.dialect"));  
   }  
 }  

Following is the corresponding Unit test

 package org.fazlan.yadic.ds;  

 import com.googlecode.yadic.Container;  
 import org.testng.annotations.Test;  
 import java.sql.SQLException;  
 import static org.fazlan.yadic.common.AbstractValueObject.valueOf;  
 import static org.fazlan.yadic.util.DataSourceObjectMother.getJdbcConfigForH2;  
 import static org.fazlan.yadic.util.DataSourceObjectMother.newDIContainer;  
 import static org.hamcrest.MatcherAssert.assertThat;  
 import static org.hamcrest.core.Is.is;  

 @Test  
 public class DataSourceConfigTest {  

   public void itInjectsInstanceAndWiresAllDependencies() throws SQLException {  

     //given  
     DataSourceConfig h2Config = getJdbcConfigForH2();  
     Container container = newDIContainer().addInstance(DataSourceConfig.class, h2Config);  

     //when  
     DataSourceConfig config = container.get(DataSourceConfig.class);  

     //then  
     assertThat(valueOf(config.getDialect()), is(valueOf(h2Config.getDialect())));  
     assertThat(valueOf(config.getUri()), is(valueOf(h2Config.getUri())));  
     assertThat(valueOf(config.getUserName()), is(valueOf(h2Config.getUserName())));  
     assertThat(valueOf(config.getPassword()), is(valueOf(h2Config.getPassword())));  
   }  
 }  

The above code keeps a reference to the instance h2Config via DataSourceConfig.class. Whenever, a dependency is required of type DataSourceConfig.class, then the object referenced by h2Config will be injected. Read here

Adding an Instance to the Container via an Activator
Activator is an implementation of a Callable. Inside a call method we define how our object should be created. Following is an example, how an activator could be used to instantiate a database connection. Read here


 package org.fazlan.yadic.persistence; 
 
 import java.io.Closeable;  
 import java.io.IOException;  
 import java.sql.Connection;  
 import java.util.concurrent.Callable;  
 import static com.googlecode.totallylazy.Closeables.safeClose;  

 public class JdbcConnectionActivator implements Callable<Connection>, Closeable {  

   private final JdbcConnectionFactory factory;  
   private Connection connection;  

   public JdbcConnectionActivator(JdbcConnectionFactory factory) {  
     this.factory = factory;  
   }  

   @Override  
   public Connection call() throws Exception {  
     connection = factory.getConnection();  
     return connection;  
   }  

   @Override  
   public void close() throws IOException {  
     safeClose(connection);  
   }  
 }  

Following is the corresponding Unit test.


 package org.fazlan.yadic.persistence;  

 import com.googlecode.yadic.Container;  
 import org.fazlan.yadic.ds.DataSourceConfig;  
 import org.testng.annotations.Test;  
 import java.sql.Connection;  
 import java.sql.SQLException;  
 import static com.googlecode.totallylazy.Closeables.safeClose;  
 import static org.fazlan.yadic.common.AbstractValueObject.valueOf;  
 import static org.fazlan.yadic.util.DataSourceObjectMother.getJdbcConfigForH2;  
 import static org.fazlan.yadic.util.DataSourceObjectMother.newDIContainer;  
 import static org.hamcrest.MatcherAssert.assertThat;  
 import static org.hamcrest.Matchers.equalToIgnoringCase;  
 import static org.hamcrest.core.Is.is;  
 import static org.hamcrest.core.IsNull.notNullValue;  

 @Test  
 public class JdbcConnectionActivatorTest {  

   public void itInjectsActivatorAndWiresAllDependencies() throws SQLException {  

     //given  
     DataSourceConfig config = getJdbcConfigForH2();  
     Container container = newDIContainer()  
         .addInstance(DataSourceConfig.class, config)  
         .add(JdbcConnectionFactory.class)  
         .addActivator(Connection.class, JdbcConnectionActivator.class); 
 
     //when  
     Connection connection = container.get(Connection.class);  

     //then  
     assertThat(connection, is(notNullValue()));  
     assertThat(connection.getMetaData().getURL(), is(valueOf(config.getUri())));  
     assertThat(connection.getMetaData().getUserName(), equalToIgnoringCase(valueOf(config.getUserName())));  

     //finally  
     safeClose(connection);  
   }  
 }  

The above adds the following to the contaniner,
- DataSourceConfig, database config related to H2 db.

- JdbcConnectionFactory, factory that creates the connection

- JdbcConnectionActivator, Activator that adds the created connection via a java.sql.Connection reference

and the container will return the connection instance created by the Activator via the java.sql.Connection.class

Step 5: Retrieving References from the Container
Retrieving the instance from the container is straight forward. You can use the get method on the container to fetch references. Read here.

Step 6: Dependency Resolution Mechanism and Auto-wiring in Yadic
How does Yadic manage the dependency resolution and auto-wiring of instance? Read here.

Summary:
This was a brief article on how to make use of Yadic as a DI framework in your development environment.

Sample code can be found here.

1 comment: