Saturday, July 28, 2018

Understanding JNDI

Video Tutorial

Related Article

Take a look at the basic JDBC code to connect to a DB and get the results-
We defined JDBC Driver,DB URL,user name and password etc in Java code.

//STEP 1. Import required packages
import java.sql.*;

public class FirstExample {
   // JDBC driver name and database URL
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";  
   static final String DB_URL = "jdbc:mysql://localhost/EMP";

   //  Database credentials
   static final String USER = "username";
   static final String PASS = "password";
   
   public static void main(String[] args) {
   Connection conn = null;
   Statement stmt = null;
   try{
      //STEP 2: Register JDBC driver
      Class.forName("com.mysql.jdbc.Driver");

      //STEP 3: Open a connection
      System.out.println("Connecting to database...");
      conn = DriverManager.getConnection(DB_URL,USER,PASS);

      //STEP 4: Execute a query
      System.out.println("Creating statement...");
      stmt = conn.createStatement();
      String sql;
      sql = "SELECT id, first, last, age FROM Employees";
      ResultSet rs = stmt.executeQuery(sql);

      //STEP 5: Extract data from result set
      while(rs.next()){
         //Retrieve by column name
         int id  = rs.getInt("id");
         int age = rs.getInt("age");
         String first = rs.getString("first");
         String last = rs.getString("last");

         //Display values
         System.out.print("ID: " + id);
         System.out.print(", Age: " + age);
         System.out.print(", First: " + first);
         System.out.println(", Last: " + last);
      }
      //STEP 6: Clean-up environment
      rs.close();
      stmt.close();
      conn.close();
   }catch(SQLException se){
      //Handle errors for JDBC
      se.printStackTrace();
   }catch(Exception e){
      //Handle errors for Class.forName
      e.printStackTrace();
   }finally{
      //finally block used to close resources
      try{
         if(stmt!=null)
            stmt.close();
      }catch(SQLException se2){
      }// nothing we can do
      try{
         if(conn!=null)
            conn.close();
      }catch(SQLException se){
         se.printStackTrace();
      }//end finally try
   }//end try
   System.out.println("Goodbye!");
}//end main
}//end FirstExample
But as she plan to move the code from dev to higher envs then number of problems like details are different......DB max connection is different etc or even the db itself is different. For ex- in dev we use mySQL but in prod it is DB2.

J2EE components, such as WAR files and EJB JAR files, must declare their dependence on resources outside their deployment units.


JNDI to the rescue

The solution to Dolly's problem is to remove all direct references to the data store from her application code. No references to JDBC drivers, no server names, no user names or passwords -- not even database pooling or connection management. 
Deployer (whoever is in that role) to allocate database connections to her application, without Dolly having to be involved.

The J2EE specification requires that all J2EE containers provide an implementation of the JNDI specification.

Dolly would be better served by using JNDI to find a JDBC DataSource, as shown in Listing 2:
Listing 2. Using JNDI to acquire a data source

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Connection conn=null;
try {
  Context ctx=new InitialContext();
  Object datasourceRef=ctx.lookup("java:comp/env/jdbc/mydatasource");
  DataSource ds=(Datasource)datasourceRef;
  conn=ds.getConnection();
  /* use the connection */
  conn.close();
}
catch(Exception e) {
  e.printStackTrace();
}
finally {
  if(conn!=null) {
    try {
      conn.close();
    } catch(SQLException e) { }
  }
}

Configuring the JNDI reference

In order for JNDI to resolve the java:comp/env/jdbc/mydatasource reference, the Deployer is required to insert a <resource-ref> tag into the web.xml file.

1
2
3
4
5
6
<resource-ref>
  <description>Dollys DataSource</description>
  <res-ref-name>jdbc/mydatasource</res-ref-name>
  <res-ref-type>javax.sql.DataSource</res-ref-type>
  <res-auth>Container</res-auth>
</resource-ref>

The <resource-ref> entry informs the servlet container that a resource in the component naming context, called jdbc/mydatasource, will be set up by the Deployer. The component naming context is indicated by the prefixjava:comp/env/, so the fully qualified local resource name is java:comp/env/jdbc/mydatasource.
That defines only the local reference to the external resource, and doesn't create the actual resource that this reference will point to.
The Deployer's job is to create a DataSource . Each container has its own mechanism for setting up data sources. Once the resource has been created, there is still a critical third step: to connect, or bind, the resource to the local name(s) used by the application components. In the case of a Web application, a vendor-specific deployment descriptor extension is used to specify this binding, an example of which is shown in Listing 4. (JBoss uses a file called jboss-web.xml for the vendor-specific Web application deployment descriptor.)
Listing 4. Binding a resource to a JNDI name in the vendor-specific deployment descriptor

1
2
3
4
<resource-ref>
   <res-ref-name>jdbc/mydatasource</res-ref-name>
   <jndi-name>java:DefaultDS</jndi-name>
</resource-ref>

There are two levels of indirection here -- one to define and name the resource (java:DefaultDS), the other to bind the local component-specific name (jdbc/mydatasource) to the named resource. (In fact, there is the possibility for a third level of indirection as you can map resources at the EAR level, as well.)