Discussions

EJB design: Reusable Service Locator COmponent

  1. Reusable Service Locator COmponent (3 messages)

    Hi,

    I would like to submit the following Service Locator implementation.
    It is meant to be a fully reusable SL implementation.
    Indeed, one of the major drawback of the proposed implementation of the book CoreJ2EEPattern, is that it hard codes the real jndi name (i.e. the jndi names assigned by the deployer) into a java class used and provided by the bean provider. So it assumes that the bean provider knows the jndi name used by the application deployer.
    This is definitely not good...
    But the following implementation leverage environment properties to avoid the problem:

    &<code&>
    /**
     * @author François Vanzeveren
     *
     */

    /**
     * @ejb.bean name = "ServiceLocator"
     * type = "Stateless"
     * display-name = "ServiceLocator"
     * description = "ServiceLocator EJB"
     * view-type = "remote"
     * jndi-name = "ejb/ServiceLocatorHome"
     * EJB References
     * ==============
     * -- Session Beans --
     * @ejb.ejb-ref ejb-name = "Test1Manager" ref-name = "ejb/Test1Manager" view-type = "remote"
     * @ejb.ejb-ref ejb-name = "Test2Manager" ref-name = "ejb/Test2Manager view-type = "remote"
     * @ejb.ejb-ref ejb-name = "Test3Manager" ref-name = "ejb/Test3Manager" view-type = "remote"
     * -- Entity Beans --
     * @ejb.ejb-ref ejb-name = "Test1Entity" ref-name = "ejb/Test1Entity" view-type = "remote"
     * @ejb.ejb-ref ejb-name = "Test1Entity" ref-name = "ejb/Test1EntityLocal" view-type = "local"
     *
     * Environnement Variables
     * =======================
     * -- Session Beans --
     * @ejb.env-entry name = "test.test1mgr.Test1ManagerHome" type = "java.lang.String" value = "ejb/Test1Manager"
     * description = ""
     * @ejb.env-entry name = "test.test2mgr.Test2ManagerHome" type = "java.lang.String" value = "ejb/Test2Manager"
     * description = ""
     * @ejb.env-entry name = "test.test3mgr.Test3ManagerHome" type = "java.lang.String" value = "ejb/Test3Manager"
     * description = ""
     * -- Entity Beans --
     * @ejb.env-entry name = "test.test1.Test1Home" type = "java.lang.String" value = "ejb/Test1"
     * description = ""
     * @ejb.env-entry name = "test.test1.Test1LocalHome" type = "java.lang.String" value = "ejb/Test1Local"
     * description = ""
     *
     * @ejb.permission unchecked = "true"
     * @ejb.transaction type = "Required"
     */
    public class ServiceLocatorBean implements SessionBean {

    private static final Logger LOGGER = Logger.getLogger(ServiceLocatorBean.class);

    private Map cachedHomes;

    /**
    * Map
    * key: EJB(Local)Home.class
    * value: JNDI Name
    */
    private Map jndiNames;

    /** The SessionContext */
    private SessionContext context;

    /**
    * @ejb.create-method
    */
    public void ejbCreate() throws CreateException {
    LOGGER.info("[IN] ejbCreate()");
    this.cachedHomes = new HashMap();
    this.jndiNames = new HashMap();
    LOGGER.info("[OUT] ejbCreate()");
    }

    public void ejbActivate() throws EJBException {
    }

    public void ejbPassivate() throws EJBException {
    }

    public void ejbRemove() throws EJBException {
    }

    public void setSessionContext(SessionContext newContext)
    throws EJBException {
    context = newContext;
    }

    // Home interface lookup methods

    /**
    * Obtain home interface (local or remote, depending on narrowTo)
    * from default initial context
    * @return Home interface of the narrowTo type.
    * @throws test.servicelocator.ServiceException if
    * the service is not registered with the Service Locator or if
    * a problem occured during the lookup.
    *
    * @ejb.interface-method view-type = "remote"
    */
    public Object getHome(Class narrowTo) throws ServiceException
    {
    return this.getHome(null, narrowTo);
    }

    /**
    * Obtain home interface (local or remote, depending on narrowTo)
    * from parameterised initial context
    * @param environment Parameters to use for creating initial context
    * @return Home interface of the narrowTo type.
    * @throws test.servicelocator.ServiceException if
    * the service is not registered with the Service Locator or if
    * a problem occured during the lookup.
    *
    * @ejb.interface-method view-type = "remote"
    */
    public Object getHome(java.util.Hashtable environment, Class narrowTo) throws ServiceException
    {
    Object home = this.cachedHomes.get(narrowTo);
    if (home == null) {
    try {
    home = lookupHome(environment, narrowTo);
    } catch (NamingException e) {
    throw new ServiceException(
    "The following error occured when looking for service '"
    + narrowTo.getName() + "' : " + e.getMessage()
    );
    }
    this.cachedHomes.put(narrowTo, home);
    }
    return home;
    }

    /**
    * Obtain home interface (local or remote, depending on narrowTo) from default initial context
    * @return Home interface of the narrowTo type.
    * @throws javax.naming.NamingException
    * @throws test.servicelocator.ServiceException if
    * the service is not registered with the Service Locator.
    */
    private Object lookupHome(java.util.Hashtable environment, Class narrowTo) throws javax.naming.NamingException, ServiceException {
    // Obtain initial context
    String jndiName = (String) this.jndiNames.get(narrowTo);
    javax.naming.InitialContext initialContext = new javax.naming.InitialContext(environment);
    if (jndiName == null) {
    Context myContext = (Context) initialContext.lookup("java:comp/env");
    jndiName = "java:comp/env/" + myContext.lookup(narrowTo.getName());
    if (jndiName == null) throw new ServiceException(narrowTo);
    this.jndiNames.put(narrowTo, jndiName);
    if (LOGGER.isInfoEnabled()) LOGGER.info(narrowTo.getName() + "\t-->\t" + jndiName);
    }
    try {
    Object objRef = initialContext.lookup(jndiName);
    // only narrow if necessary
    if (narrowTo.isInstance(java.rmi.Remote.class))
    return javax.rmi.PortableRemoteObject.narrow(objRef, narrowTo);
    else
    return objRef;
    } finally {
    initialContext.close();
    }
    }
    &<&/code&>

    A client can access the registered bean as follow:
    &<code&>
    serviceLocator.getHome(test.test1mgr.Test1ManagerHome.class);
    &<&/code&>

    The advantages of this implementation are:
     - No jndi name is hard coded
     - The application deployer must match the ejb references in only one place of the DD, i.e. the ServiceLocator section.
     - The application assembler has just to ensure that every bean has a reference to the ServiceLocator.
     - In the web.xml, only one ejb-ref tag (the one for Service Locator
     - This implementation is reusable as is for any application. No code change, just adapt the DD.

    I would appreciate comments.

    Regards

    François

    Threaded Messages (3)

  2. Additional comment[ Go to top ]

    I was wrong in the introduction when saying that the Singleton implementation requires the EJB provider to know the real jndi-names.
    It is not true as long as the bean provider declares all the bean references in the DD.
    But, i find it tedious to have to declare in every bean all the beans it is using, and the Singleton implemnation does not allow to avoid that.
    The implemnation described in my post dramatically reduce declaration.
    EJB needs only to know about the ServiceLocator, not the others, while the
    Service Locator knows about all the beans.

    Hope it helps

    François
  3. Is it advisable to use "this" as the key word in Session Beans.

    Regards,
    Bharath.B.
  4. WHat do you mean?[ Go to top ]

    Sorry, but I do not see what you mean..

    Regards


    François