blogger templates blogger widgets
This is part of a list of blog posts.
To browse the contents go to

Bind and lookup - references - FS service provider

First let's create a class whose instance we will bind it with a name and store the name and object reference in naming service.
package com.sample;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;

public class Fruit implements Referenceable {
 String fruit;

 public Fruit(String f) {
  fruit = f;
 }

 public Reference getReference() throws NamingException {
  System.out.println("getReference");
  String className = Fruit.class.getName();
  StringRefAddr classref = new StringRefAddr("fruit", fruit);
  Reference nameObj = new Reference(className, classref);
  return nameObj;
 }

 public String toString() {
  return fruit;
 }
}

Any class that needs to be stored in a initialContext needs to implement Referenceable and so override getReference() method that returns a Reference object.

Objects of type Reference represents a reference to an object that is found outside of the naming/directory system. Meaning, that object is simply a reference holder to another object(in our case, Fruit object).

Here is what API says about Reference,

"A Reference consists of an ordered list of addresses and class information about the object being referenced. Each address in the list identifies a communications endpoint for the same conceptual object."

package com.sample;

import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;

public class Fruit implements Referenceable {
 String fruit;

 public Fruit(String f) {
  fruit = f;
 }

 public Reference getReference() throws NamingException {
  System.out.println("getReference");
  String className = Fruit.class.getName();
  StringRefAddr classref = new StringRefAddr("fruit", fruit);
  Reference nameObj = new Reference(className, classref);
  return nameObj;
 }

 public String toString() {
  return fruit;
 }
}

And here is our test class,

package com.sample;

import java.util.Hashtable;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TestJNDI2 {

 public static void main(String[] args) throws NamingException {
  Hashtable<String, String> env = new Hashtable<String, String>(2);
  env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");
  //one more configuration is required for binding
  env.put(Context.PROVIDER_URL, "file:/C:/Users/eIPe/Desktop/temp");  
  Context ctx = new InitialContext(env);
  
  // Create the object to be bound
     Fruit fruit = new Fruit("orange");
     
     //unbind if there is a previous binding with same name "favorite"
     ctx.unbind("favorite"); 
     System.out.println("before Binding");
     // Perform the bind
     ctx.bind("favorite", fruit);
     System.out.println("after Binding");
     // Check that it is bound
     Object obj = ctx.lookup("favorite");
     System.out.println(obj);

     // Close the context when we're done
     ctx.close();

 }
}

Output:
before Binding
getReference
after Binding
Reference Class Name: com.sample.Fruit
Type: fruit
Content: orange

Note the output of printing the reference object,
Reference Class Name: com.sample.Fruit
Type: fruit
Content: orange
This is the output that came from Reference's toString().

If we check our directory Desktop/temp we find a new file created named .bindings

It's contents are
#This file is used by the JNDI FSContext.
#Sun Jan 26 19:08:13 IST 2014
favorite/RefAddr/0/Type=fruit
favorite/RefAddr/0/Content=orange
favorite/RefAddr/0/Encoding=String
favorite/ClassName=com.sample.Fruit

How do we get back our actual (fruit) object from this Reference object?

We can't. What we get is - what defines the actual object and which is the attributes it possess. In our case, the instance variable "fruit".

Knowing the state of instance variables, we can recreate the original object.

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.naming.RefAddr;
import javax.naming.Reference;


public class TestJNDI2 {

 public static void main(String[] args) throws NamingException {
 Hashtable<String, String> env = new Hashtable<String, String>(2);
  env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");
  //this configuration is required for binding
  env.put(Context.PROVIDER_URL, "file:/C:/Users/eIPe/Desktop/temp");
  
  Context ctx = new InitialContext(env);
  
  // Create the object to be bound
     Fruit fruit = new Fruit("orange");
     
     //unbind if there is a previous binding with same name "favorite"
     ctx.unbind("favorite"); 
     System.out.println("before Binding");
     // Perform the bind
     ctx.bind("favorite", fruit);
     System.out.println("after Binding");
     // Check that it is bound
     Reference ref = (Reference) ctx.lookup("favorite");

     System.out.println(ref);
     RefAddr addr = ref.get("fruit");
     Fruit new_fruit = new Fruit((String) addr.getContent());
     System.out.println(new_fruit);
     // Close the context when we're done
     ctx.close();   
     // Close the context when we're done
     ctx.close();
 }
}

A more code reusable way would be to provide a factory class that will do this re-creation of objects when necessary.

We achieve this by using another overloaded constructor of Reference class.

Reference(String className, RefAddr addr, String factory, String factoryLocation)

Constructs a new reference for an object with class name 'className', the class name and location of the object's factory, and the address for the object.

We create a factory class by implementing ObjectFactory interface and overriding the method getObjectInstance().

The method is defined as:
Object getObjectInstance(Object obj, Name name, Context nameCtx, Hashtable<?,?> environment) throws Exception

Creates an object using the location or reference information specified. Special requirements of this object are supplied using environment. An example of such an environment property is user identity information.

NamingManager.getObjectInstance() successively loads in object factories and invokes this method on them until one produces a non-null answer. When an exception is thrown by an object factory, the exception is passed on to the caller of NamingManager.getObjectInstance() (and no search is made for other factories that may produce a non-null answer).

An object factory should only throw an exception if it is sure that it is the only intended factory and that no other object factories should be tried. If this factory cannot create an object using the arguments supplied, it should return null.


import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;

public class Fruit implements Referenceable {
 String fruit;

 public Fruit(String f) {
  fruit = f;
 }

 public Reference getReference() throws NamingException {
  System.out.println("getReference");
  String className = Fruit.class.getName();
  StringRefAddr classref = new StringRefAddr("fruit", fruit);
  String factoryClassName = FruitFactory.class.getName();
  Reference nameObj = new Reference(className, classref, factoryClassName, null);
  return nameObj;
 }

 public String toString() {
  return fruit;
 }
}

import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
/**
 * This is an object factory that when given a reference for a Fruit
 * object, will create an instance of the corresponding Fruit.
 */
public class FruitFactory  implements ObjectFactory {
 public FruitFactory() {
 }

 public Object getObjectInstance(Object obj, Name name, Context ctx,
   Hashtable env) throws Exception {
  if (obj instanceof Reference) {
   Reference ref = (Reference) obj;
   if (ref.getClassName().equals(Fruit.class.getName())) {
    RefAddr addr = ref.get("fruit");
    if (addr != null) {
     return new Fruit((String) addr.getContent());
    }
   }
  }
  return null;
 }
}

And now the test class,

package com.sample;
import java.util.Hashtable;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

public class TestJNDI2 {

 public static void main(String[] args) throws NamingException {
  Hashtable<String, String> env = new Hashtable<String, String>(2);
  env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");
  //this configuration is required for binding
  env.put(Context.PROVIDER_URL, "file:/C:/Users/eIPe/Desktop/temp");
  
  Context ctx = new InitialContext(env);
  
  // Create the object to be bound
     Fruit fruit = new Fruit("orange");
     
     //unbind if there is a previous binding with same name "favorite"
     ctx.unbind("favorite"); 
     System.out.println("before Binding");
     // Perform the bind
     ctx.bind("favorite", fruit);
     System.out.println("after Binding");
     // Check that it is bound
     Fruit new_fruit = (Fruit) ctx.lookup("favorite");
     System.out.println(new_fruit);
    
     // Close the context when we're done
     ctx.close();

 }
}

Note that instead of calling unbind, we could use rebind() that will add or replace a binding.
ctx.rebind("favorite", fruit); 

How do we manage a more complex object?

Let's say Fruit has 2 attributes.

package com.sample;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;

public class Fruit implements Referenceable {
 String fruit;
 String taste;


 public Fruit(String f, String t) {
  fruit = f;
  taste = t;
 }

 public Reference getReference() throws NamingException {
  String className = Fruit.class.getName();
  StringRefAddr fruitref = new StringRefAddr("fruit", fruit);
  StringRefAddr tasteref = new StringRefAddr("taste", taste);
  String factoryClassName = FruitFactory.class.getName();
  Reference refObj = new Reference(className, factoryClassName, null);
  refObj.add(fruitref);
  refObj.add(tasteref);
  return refObj;
 }

 public String toString() {
  return fruit+" tastes "+taste;
 }
}

package com.sample;
import java.util.Collections;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.List;

import javax.naming.Context;
import javax.naming.Name;
import javax.naming.RefAddr;
import javax.naming.Reference;
import javax.naming.spi.ObjectFactory;
/**
 * This is an object factory that when given a reference for a Fruit
 * object, will create an instance of the corresponding Fruit.
 */
public class FruitFactory  implements ObjectFactory {
 public FruitFactory() {
 }

 public Object getObjectInstance(Object obj, Name name, Context ctx,
   Hashtable env) throws Exception {
  
  if (obj instanceof Reference) {
   Reference ref = (Reference) obj;
   if (ref.getClassName().equals(Fruit.class.getName())) {
    Enumeration<refaddr> refaddrs = ref.getAll();
    List<refaddr> fruitAttributes = Collections.list(refaddrs);
    if (fruitAttributes != null && fruitAttributes.size()>1) {
     return new Fruit((String)fruitAttributes.get(0).getContent(), (String)fruitAttributes.get(1).getContent());
    }
   }
  }
  return null;
 }
}

The Test class,

public class TestJNDI2 {

 public static void main(String[] args) throws NamingException {
  Hashtable<String, String> env = new Hashtable<String, String>(2);
  env.put(Context.INITIAL_CONTEXT_FACTORY,
    "com.sun.jndi.fscontext.RefFSContextFactory");
  //this configuration is required for binding
  env.put(Context.PROVIDER_URL, "file:/C:/Users/eIPe/Desktop/temp");
  
  Context ctx = new InitialContext(env);

     Fruit fruit = new Fruit("orange","sweet");

     ctx.rebind("favorite", fruit);

     Fruit newFruit = (Fruit) ctx.lookup("favorite");
     System.out.println(newFruit);
    
     ctx.close();

 }
}

No comments:

Post a Comment