Tuesday, 14 April 2015

CREATING A DATASOURCE DEFINITION USING ANNOTATION @DataSourceDefinition : JAVA EE 7

In an enterprise application, the most important and a necessary task is to persist (save)
the data into the database.(Typically the data entered by the user in  the front end).
In order to persist the data, the developer needs to make a connection to the data source.

Earlier, even today most of the legacy code deals with JDBC boilerplate code- which was conventional way of doing the things.

We know that in order to connect to  database from the java classes is to use a datasource defintion created in an application server.

We generally use a jdbc connection resource which is pre-configured in the application server. But, In Java EE 7, you can create a Data Source using an annotation  @DataSourceDefinition, which takes some necessary parameters- like

  • a JNDI name for the connection, 
  • Database name, 
  • Database credentials: username & password
  • relative database driver
  • and a Database URL for the connection. 

Syntax:

@DataSourceDefinition(
        name="<<JNDI name  for Data Source>>",
        className="<<Database Driver Class Name>>",
        url="<<Database URL>>",
        user="<<Database User>>",
        password="<<Password>>") 

We place this annotation on any container managed bean like say on EJB.
First, the container will deploy the bean and then create the data source.

Generally, developers use the application server user interface to do this. But this way of doing is easy and time saving. Because you don't need to use the user interface by logging into it.

In this post, I have first created an EJB which registers this data source. And to test the data source created after the application deployment I would write a simple test to check the data source.

Instead of deploying the application into an application server, I would want to use an Embedded Glassfish EJB Container, this makes easy deployment and there is no need of any installation of any particular Application Server.

You can any other Embedded Container(Java EE 7 compliant) like WELD to test this.

Here, I have created a Maven project - these are some of the required artifacts.
Note: If your new to maven please read this post once More on Maven ....

Required maven artifacts:

1) Java(TM) EE 7 Specification APIs » 7.0

        <dependency>
            <groupId>javax</groupId>
            <artifactId>javaee-api</artifactId>
            <version>7.0</version>
            <scope>provided</scope>
        </dependency>

As we are writing some EJB code - we need a javaee-api artifact. Observer its scope: provided - because artifact is provided by Embedded Glassfish Server.

2) Embedded GlassFish All In One
        <dependency>
            <groupId>org.glassfish.main.extras</groupId>
            <artifactId>glassfish-embedded-all</artifactId>
            <version>4.1</version>
        </dependency>

Most important dependency to be added. Embedded Glassfish Server enables you run Glassfish Server inside of any virtual machine for Java Platform ( Java Virtual Machine or JVMmachine). No installation or configuration of embedded GlassFish Server is required. Just add this dependency to the POM.xml of your maven project.

3) MySQL java connector
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.34</version>
        </dependency>

This dependency is used to connect the Mysql Database - contains required Driver class. you can use any other databases. I would recommend you to use the embedded databases like Derby, H2, HSQL.

4)  JUnit
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

In order to test the data source created - you need any testing framework.
you can use TestNG testing framework instead.

Example:

This Example contains only two classes -
DataSourceDefinitionConfig.java - EJB
TestDataSource.java - Test Class

This EJB acts as a marker - because it does not have any code in it. Just the declaration of the annotation. This class created under src/main/java in the maven project structure.

As we know there are 3 types of Session Beans: Stateful, Stateless, Singleton.
A Singleton session bean is shared between clients and supports concurrent access.
The container will make sure that only one instance exits for entire application. So by making this data source definition as a singleton we can ensure - it is been created only once and shared by all the users of the application.

@Singleton - indicates this session bean. @Startup indicates the container invokes this bean class during the application startup.

package com.config;

import javax.annotation.sql.DataSourceDefinition;
import javax.ejb.Singleton;
import javax.ejb.Startup;


@Singleton
@Startup
@DataSourceDefinition(
  name="java:global/jdbc/MyOwnDatasource",
  className="com.mysql.jdbc.Driver",
  url="jdbc:mysql://localhost:3306/mydatabase",
  databaseName="mydatabase",
  user="root",
  password="root")
public class DataSourceDefinitionConfig {
}

 JUnit Test Class: created under src/test/java

package com.test;

import static org.junit.Assert.assertNotNull;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import javax.ejb.embeddable.EJBContainer;
import javax.naming.Context;
import javax.naming.NamingException;

import org.junit.Assert;
import org.junit.Test;

import com.service.BlogService;

public class TestDataSource {

 @Test
 public void testDataSourceDefintion() {

  Map properties = new HashMap<>();
  properties.put(EJBContainer.MODULES, new File("target/classes"));

  try (EJBContainer ec = EJBContainer.createEJBContainer(properties)) {

   Context ctx = ec.getContext();

   // Check JNDI dependencies (Datasource)
   try {
    assertNotNull(ctx.lookup("java:global/classes/DataSourceDefinitionConfig"));
    assertNotNull(ctx.lookup("java:global/jdbc/MyOwnDatasource"));
       } catch (NamingException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
   }

  }

 }
 }


Here, Embedded Container object is created in try-with-resource statement, so that you don't need to close the container.

Embedded Container instance requires some properties - Here, they are supplied as a Map.

EJBContainer.MODULES  as  target/classes

Container requires the path for the classes. when we run a maven goal : mvn package or in this case mvn test - this would generate a target folder in your project, which in turn contains classes folder. so provide the path for classes in target folder as a property.

In the @Test method - we are testing whether the session bean is loaded or not and also testing whether this session bean (EJB) created the data source or not.

We need to lookup the session beans and data source resource in Context. For this, first get the Context object from the instance of EJBContainer. 
                                     
                                             Context ctx = ec.getContext();

          assertNotNull(ctx.lookup("java:global/classes/DataSourceDefinitionConfig"));
          assertNotNull(ctx.lookup("java:global/jdbc/MyOwnDatasource"));

Note:

Whenever an EJB is deployed into an application server, the server would provide a JNDI name for all the beans deployed. If you could see the console while this test runs - you would observe this JNDI name- server provides a JNDI name for the session bean :

 DataSourceDefinitionConfig.java as
java:global/classes/DataSourceDefinitionConfig.

Actually every session bean does not get same form of JNDI names - but it depends on the interface views that session beans is using. I would recommend you to know more on portable JNDI names. More on Portable JNDI names

Generally JNDI names for EJBs start with either of these namespaces :

java:global/
java:app/
java:module/

Similarly, A Data Source is also a Server Resource - so It would also have a JNDI name :  java:global/jdbc/MyOwnDatasource

Note: This JNDI name is what we have provided in its declaration.
 Try to provide a prefix like this "java:global/jdbc/<<your own name>>"

SOME EXAMPLES ON VARIOUS DATABASES:

H2 Database

@DataSourceDefinition(
        name="java:app/MyDatasource",
        className="org.h2.jdbcx.JdbcDataSource",
        url="jdbc:h2:mem:test")

H2 Database - is an in memory database. Does not requires any installation of the database. But you must add a  dependency for h2 database in your maven POM.xml file. Know more on : H2 Database URL format...

Maven Dependency :

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.186</version>
        </dependency>

DERBY DATABASE

@DataSourceDefinition(
className = "org.apache.derby.jdbc.EmbeddedDataSource",
name = "java:global/jdbc/NewDS",
user = "app",
password = "app",
databaseName = "NewDS",
properties = {"connectionAttributes=;create=true"}
)
DERBY database: creates a embedded database and also creates a data source.

Maven Dependency :

        <dependency>
            <groupId>org.apache.derby</groupId>
            <artifactId>derby</artifactId>
            <version>10.10.1.1</version>
        </dependency>

This example is extended in the source code provided down.
In the source code an entity is persisted into the database by using this data source
created with @DataSourceDefinition.

Note: you must provide your JNDI name in the jta-data-source in persistence.xml file.

Once after running the test- check your MySQL database - A table named "BLOG" will be created.