AdSense Mobile Ad

Tuesday, September 28, 2010

EJB 3.1 Global JNDI Access

Table of Contents


As outlined in the previous parts of this series, the major drawback of the EJB v. 3.0 Specification was the lack of portable global JNDI names. This implies that there's no portable way to link EJB references to a bean outside your application.

The EJB v. 3.1 Specification fills this gap defining, in its own words:

"a standardized global JNDI namespace and a series of related namespaces that map to the various scopes of a Java EE application."

This blog post will give you an overview of the Global JNDI Access as defined by the EJB v. 3.1 Specification.

Namespaces and Scopes

The EJB v. 3.1 Specification defines three distinct namespaces with its own scopes:
  • Global.
  • Application.
  • Module.

The specification requires compliant containers to register all of the session beans with the required JNDI names. Such standardized names are portable and your application components will be able to establish a reference to an EJB using a name that is portable across application servers.

Global

Names in the global namespace will be accessible to code in any application and conform to the following syntax:

java:global[/<app-name>]/<module-name>/<bean-name>[!<interface-FQN>]

<app-name> is the name of the Java EE application as specified in its standard deployment descriptor (application.xml) or, by default, the base name of the deployed EAR archive. This path fragment is used only if the session EJB is deployed within a Java EE application EAR file.

If the session EJB is deployed in an EAR file, its <module-name> is the path name of the Java EE module that contains the bean (without extension) inside the EAR file. If the session bean is deployed as a standalone Java EE component in a JAR file or as part of a Java EE web module in a WAR file (which is now allowed by the Java EE 6 Specification), the <module-name> is the name of the archive (without extension). The <module-name> value can be overridden by the <module-name/> element of the component's standard deployment descriptor (ejb-jar.xml or web.xml).

The <bean-name> is the EJB name as specified by the mechanisms described in the previous parts of this blog post.

The <interface-FQN> part is the fully qualified name of the EJB business interface.

The container has to register one JNDI global entry for every local and remote business interface implemented by the EJB and its no-interface view.

Session EJB With One Business Interface or a No-Interface View

If an EJB implements only one business interface or only has a no-interface view, the container is also required to register such a view with the following JNDI name:

java:global[/<app-name>]/<module-name>/<bean-name>

Application

Names in the application namespace will be accessible only to code within the same application and conform to the following syntax:

java:app/<module-name>/<bean-name>[!<interface-FQN>]

Each path fragment retains the same meaning described for the global namespace JNDI names syntax in the previous section.

The same publishing rules for a compliant container described in the previous section apply.

Module

Names in the module namespace will be accessible only to code within the same module and conform to the following syntax:

java:module/<bean-name>[!<interface-FQN>]

Once more, each path fragment retains the same meaning described for the global namespace JNDI names.

The same publishing rules for a compliant container described in the global namespace section.

Local Clients

It may be important to notice that, although global JNDI names for local interfaces (and no-interface views) are published, this does not imply that such an interface will be accessible to components running in another JVM.

Conclusions

The EJB v. 3.1 Specification, and other specifications in the Java EE 6 Platform, brings simplicity and adds many new features and tools to the developers' toolboxes. "Global JNDI names" is an outstanding, although simple, feature because it finally fills a long-standing portability limitation of the previous revisions of this specification and, hence, of the entire Java EE platform.

EJB 3.0 and EJB 3.1 provide a powerful, portable, yet simple component model to build enterprise applications. The "EJB sucks" day are gone but only time will tell if this technology will regain the trust of Us, The Developers.

As far as it concerns myself, I feel pretty comfortable with Java EE 6, EJBs, CDI beans, the good support I have from IDEs such as NetBeans or JDeveloper (although the latter does not support EJB 3.1 yet), and all the specifications that build up this venerable platform.

A Shell Script to Find and Remove the BOM Marker

Edited:
As pointed out by Omri, the script is failing on OS X apparently because of an idiosyncrasy in Apple's sed implementation. I temporarily fixed the script switching from sed to perl on OS X: perl is also shipped by default on OS X so there shouldn't be any problem. However, on OS X this version of the script scans by default the entire file, and not only the first line as it does with other sed implementations.

Introduction

Have you ever seen this characters while dumping the contents of some of your text files, haven't you?



If you have, you found a BOM marker! The BOM marker is a Unicode character with code point U+FEFF that specifies the endianness of an Unicode text stream.

Since Unicode characters can be encoded as a multibyte sequence with a specific endianness, and since different architectures may adopt distinct endianness types, it's fundamental to signal the receiver about the endianness of the data stream being sent. Dealing with the BOM, then, it's part of the game.

If you want to know more about when to use the BOM you can start by reading this official Unicode FAQ.

This post has been modified to solve some problems and improve the script according to your comments:

  • Solved some mktemp inconsistencies across UNIX flavours (such as Solaris and Mac OS X).
  • Files can now be filtered by extension using the -e option, as suggested by Goldan.
  • BOM can be removed throughout the file using the -a option, as suggested by Goldan.
  • An arbitrary number of files can be safely passed as a parameter.
  • The script behaves correctly even with filenames with whitespaces in it.
Safe Harbour Statements: I try to test the script in the greatest number of systems but I'm not guaranteeing that it is working correctly on your. I'll be glad if you give me your feedback: any suggestion or bug report will be appreciated.

UTF-8

UTF-8 is one of the most widely used Unicode characters encoding on software and protocols that have to deal with textual data stream. UTF-8 represents each Unicode character with a sequence of 1 to 4 octects. Each octect contains control bits that are used to identify the beginning and the length of an octect sequence. The Unicode code point is simply the concatenation of the non control bits in the sequence. One of the advantages of UTF-8 is that it retains backwards compatibility with ASCII in the ASCII [0-127] range since such characters are represented with the same octect in both encodings.

If you feel curious about how the UTF-8 encoding works, I've written an introductory post about it.

Common Problems
Because of its design, the UTF-8 encoding is not endianness-sensible and using the BOM with this encoding is discouraged by the Unicode standard. Unfortunately some common utilities, notably Microsoft Notepad, keep on adding a BOM in your UTF-8 files thus breaking those application that aren't prepared to deal with it.

Some programs could, for example, display the following characters at the beginning of your file:



A more serious problem is that a BOM will break a UNIX shell script interfering with the shebang (#!).

A Shell Scripts to Check for BOMs and Remove Them

The Byte Order Mark (BOM) is a Unicode character with code point U+FEFF. Its UTF-8 representation is the following sequence of 3 octects:

1110 1111 1011 1011 1011 1111
E    F    B    B    B    F

The quickest way I know of to process a text file and perform this operation is sed. The following syntax will instruct sed to remove the BOM from the first line of its input file:

sed '1 s/\xEF\xBB\xBF//' < input > output

A Warning for Solaris Users
I haven't found a way (yet) to correctly use a sed implementation bundled with Solaris 10 to perform this operation, neither using /usr/bin/sed nor /usr/xpg4/bin/sed. If you're a Solaris user, please consider installing GNU sed to use the following script.

The quickest way to install sed and a lot of fancy Solaris packages is using Blastwave or OpenCSW. I've also written a post about loopback-mounting Blastwave/OpenCSW installation directory in Solaris Zones to simplify Blastwave/OpenCSW software administration.

A Suggestion for Windows Users
If you want to execute this script in a Windows environment, you can install CygWin. The base install with bash and the core utilities will be sufficient for this script to work on your CygWin environment.

Source
This is the source code of a skeleton implementation of a bash shell script that will remove the BOM from its input files. The script support recursive scanning of directories to "clean" an entire file system tree and a flag (-x) to avoid descending in a filesystem mounted elsewhere. The script uses temporary files while doing the conversion and the original file will be overwritten only if the -d option is not specified.



#!/bin/bash


set -o nounset
set -o errexit


DELETE_ORIG=true
DELETE_FLAG=""
RECURSIVE=false
PROCESSALLFILE=false
PROCESSING_FILES=false
PROCESSALLFILE_FLAG=""
SED_EXEC=sed
USE_EXT=false
FILE_EXT=""
TMP_CMD="mktemp"
TMP_OPTS="--tmpdir="
XDEV=""
ISDARWIN=false


if [ $(uname) == "SunOS" ] ; then
  if [ -x /usr/gnu/bin/sed ] ; then
    echo "Using GNU sed..."
    SED_EXEC=/usr/gnu/bin/sed
  fi 
  TMP_OPTS="-p "
fi


if [ $(uname) == "Darwin" ] ; then
  TMP_OPTS="-t tmp"

  SED_EXEC="perl -pe"
  echo "Using perl..."
  ISDARWIN=true

fi


function usage() {
  echo "bom-remove [-adrx] [-s sed-name] [-e ext] files..."
  echo ""
  echo "  -a    Remove the BOM throughout the entire file."
  echo "  -e    Look only for files with the chosen extensions."
  echo "  -d    Do not overwrite original files and do not remove temp files."
  echo "  -r    Scan subdirectories."
  echo "  -s    Specify an alternate sed implementation."
  echo "  -x    Don't descend directories in other filesystems."
}


function checkExecutable() {
  if ( ! which "$1" > /dev/null 2>&1 ); then
    echo "Cannot find executable:" $1
    exit 4
  fi
}


function parseArgs() {
  while getopts "adfrs:e:x" flag
  do
    case $flag in
      a) PROCESSALLFILE=true ; PROCESSALLFILE_FLAG="-a" ;;
      r) RECURSIVE=true ;;
      f) PROCESSING_FILES=true ;;
      s) SED_EXEC=$OPTARG ;;
      e) USE_EXT=true ; FILE_EXT=$OPTARG ;;
      d) DELETE_ORIG=false ; DELETE_FLAG="-d" ;;
      x) XDEV="-xdev" ;;
      *) echo "Unknown parameter." ; usage ; exit 2 ;; 
    esac
  done


  shift $(($OPTIND - 1))



  if [ $# == 0 ] ; then
    usage;
    exit 2;
  fi



  # fixing darwin
  if [[ $ISDARWIN == true && $PROCESSALLFILE == false ]] ; then
    PROCESSALLFILE=true
    echo "Process all file is implicitly set on Darwin."
  fi

  FILES=("$@")


  if [ ! -n "$FILES" ]; then
    echo "No files specified. Exiting."
  fi


  if [ $RECURSIVE == true ]  && [ $PROCESSING_FILES == true ] ; then
    echo "Cannot use -r and -f at the same time."
    usage
    exit 1
  fi


  checkExecutable $SED_EXEC
  checkExecutable $TMP_CMD
}


function processFile() {
  if [ $(uname) == "Darwin" ] ; then
    TEMPFILENAME=$($TMP_CMD $TMP_OPTS)
  else
    TEMPFILENAME=$($TMP_CMD $TMP_OPTS"$(dirname "$1")")
  fi
  echo "Processing $1 using temp file $TEMPFILENAME"


  if [ $PROCESSALLFILE == false ] ; then 
    cat "$1" | $SED_EXEC '1 s/\xEF\xBB\xBF//' > "$TEMPFILENAME"
  else
    cat "$1" | $SED_EXEC 's/\xEF\xBB\xBF//g' > "$TEMPFILENAME"
  fi


  if [ $DELETE_ORIG == true ] ; then
    if [ ! -w "$1" ] ; then
      echo "$1 is not writable. Leaving tempfile."
    else
      echo "Removing temp file..."
      mv "$TEMPFILENAME" "$1"
    fi
  fi
}


function doJob() {
  # Check if the script has been called from the outside.
  if [ $PROCESSING_FILES == true ] ; then
    for i in $(seq 1 ${#FILES[@]})
    do
      echo ${FILES[$i-1]}
      processFile "${FILES[$i-1]}"
    done


  else
    # processing every file
for i in $(seq 1 ${#FILES[@]})
do
CURRFILE=${FILES[$i-1]}
      # checking if file or directory exist
      if [ ! -e "$CURRFILE" ] ; then echo "File not found: $CURRFILE. Skipping..." ; continue ; fi
      
      # if a paremeter is a directory, process it recursively if RECURSIVE is set
      if [ -d "$CURRFILE" ] ; then
        if [ $RECURSIVE == true ] ; then
          if [ $USE_EXT == true ] ; then
            find "$CURRFILE" $XDEV -type f -name "*.$FILE_EXT" -exec "$0" $DELETE_FLAG $PROCESSALLFILE_FLAG -f "{}" \;
          else
            find "$CURRFILE" $XDEV -type f -exec "$0" $DELETE_FLAG $PROCESSALLFILE_FLAG -f "{}" \;
          fi
        else
          echo "$CURRFILE is a directory. Skipping..."
        fi
      else
        processFile "$CURRFILE"
      fi
    done
  fi
}


parseArgs "$@"
doJob




Examples
Assuming the script is in your $PATH and it's called bom-remove, you can "clean" a bunch of files invoking it this way:

$ bom-remove file-to-clean ...

If you want to clean the files in an entire directory, you can use the following syntax:

$ bom-remove -r dir-to-clean ...

If your sed installation is not in your $PATH or you have to use an alternate version, you can invoke the script with the following syntax:

$ bom-remove -s path/to/sed file-to-clean ...

If you want to clean a directory in which other file systems might be mounted, you can use the -x option so that the script does not descend them:

$ bom-remove -xr dir-to-clean ...

Next Steps

The most effective way to fight the BOM is avoiding spreading it. Microsoft Notepad, if there's anybody out there using it, isn't the best tool to edit your UTF-8 files so, please, avoid it.

However, should your file system be affected by the BOM-desease, I hope this script will be a good starting point to build a BOM-cleaning solution for your site.

Enjoy!






Sunday, September 26, 2010

References to EJBs Outside Your Application With Oracle WebLogic

Table of Contents


In the previous posts we made an overview of the EJB v. 3.0 and of the portable mechanisms it provides you to build your Java EE application. Since Java EE specifications are all about portability, at the risk of repeating ourselves we've often stressed the most important portability limit still present on the EJB v. 3.0 specifications: there is no portable way to declare and link a reference to an EJB outside your application.

Although there exist other standards (such as Web Services) that let you loosely couple components of your applications, remote EJBs are still an ideal mean to accomplish this task because of their simplicity, their standardization, the good development support of many IDEs and the good performance they provide.

In this blog post we'll make an overview of the mechanisms provided by one of the leading Java EE application servers, Oracle WebLogic, to give support to references to EJBs outside the scope of your application.

mappedName in Oracle WebLogic

Java EE compliant application servers provide additional non-portable API, tools and mechanisms used to enhance the standard Java EE features they implement. One of the features we've mentioned in the first part of this series was the mappedName @EJB element.

Although the EJB v. 3.0 Specification is willingly unclear about this element (that was sort of replaced by the lookup element introduced by the EJB v. 3.1 Specification), many application server vendors have implemented it with the intuitive behavior suggested by its own name: mapping an EJB to a global JNDI name.

If you want to trade portability for simplicity, many application servers (such as Oracle WebLogic or GlassFish) will let you define a bean's global JNDI name with the value of the mappedName element (or its corresponding deployment descriptor element.) As already stated, beware that Oracle WebLogic will assign global JNDI names to remote business interfaces only. This isn't really a limitation since local business interfaces can always be referenced using the APIs described in the previous parts of this series.

Oracle WebLogic Naming Conventions for EJB Remote Business Interfaces

With such a mechanism in place, linking a reference to a bean outside your application is straightforward. JDeveloper's EJB wizard, indeed, will put a default mappedName for you with an intuitive naming scheme that resembles somehow the new portable global JNDI names introduced by the EJB v. 3.1 specification, as shown in the following screenshot:


The naming scheme suggested by JDeveloper is the following:

mappedName = [application-name]-[module-name]-[bean-name]

If adopted, this naming scheme provides an easy way to assign every bean an unique name throughout your applications. I recognize that such names are a bit awkward but, being non-portable, is a naming scheme as good as any other.

Global JNDI Names for Remote EJB Interfaces in Oracle WebLogic

The global JNDI names of the remote business interfaces of an EJB with a mappedName in Oracle WebLogic Application Server will be:

mappedName#[interface-FQN]

Using this naming scheme will allow you to build loosely coupled Java EE application that reuse each other EJBs. If you want to inspect your server JNDI tree and check the actual names of your deployed EJB, you can use WebLogic's JNDI Tree inspector, which can be launched opening the WebLogic console, navigating to the Environment/Servers/[your-server] page and using the View JNDI Tree link. In the following screenshot you can examine the global JNDI entry for a bean defined as follows:

package es.reacts;

import ...;

@Stateless(name = "EJBByMappedName", mappedName = "Application1-EjbTest0-EJBByMappedName")
@Remote
public class EJBByMappedNameBean implements RemoteByMappedName {
  [...]
}


In the screenshot you can appreciate the entry corresponding to the es.reacts.RemoteByMappedName business interface.

Customizing the JNDI Name of an EJB Remote Interface

Oracle WebLogic provides you the necessary tools to customize and override its default naming conventions for EJB remote interfaces. To assign or override the global JNDI name of an EJB remote interface you can use the WebLogic specific deployment descriptors. In the case of an EJB module, for example, you can use JDeveloper to quickly add a default weblogic-ejb-jar.xml or, if using another IDE such as NetBeans, you can create a new XML file named weblogic-ejb-jar.xml in the META-INF directory of your module. An empty weblogic-ejb-jar.xml file looks like this (as of Oracle WebLogic 10.3):

<?xml version = '1.0' encoding = 'UTF-8'?>
<weblogic-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-ejb-jar http://www.bea.com/ns/weblogic/weblogic-ejb-jar/1.0/weblogic-ejb-jar.xsd"
  xmlns="http://www.bea.com/ns/weblogic/weblogic-ejb-jar">
</weblogic-ejb-jar>

To assign or override a global JNDI name for a given EJB remote interface you can use the following elements:

<?xml version = '1.0' encoding = 'UTF-8'?>
<weblogic-ejb-jar xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-ejb-jar http://www.bea.com/ns/weblogic/weblogic-ejb-jar/1.0/weblogic-ejb-jar.xsd"
  xmlns="http://www.bea.com/ns/weblogic/weblogic-ejb-jar">
  <weblogic-enterprise-bean>
    <ejb-name>SessionTest0</ejb-name>
    <stateless-session-descriptor>
      <business-interface-jndi-name-map>
        <business-remote>es.reacts.SessionTest0</business-remote>
        <jndi-name>global-jndi-name</jndi-name>
      </business-interface-jndi-name-map>
    </stateless-session-descriptor>
  </weblogic-enterprise-bean>
</weblogic-ejb-jar>

If you're using JDeveloper, it provides you an easy GUI to edit the weblogic-ejb-jar.xml file:


The JDeveloper GUI will let you easily customize the WebLogic deployment descriptor and configure other non-portable features of the WebLogic Application Server such as EJB clustering.

Linking an EJB Reference to a Global JNDI Name

In the previous section we made an overview of the tools that Oracle WebLogic Application Server provides to customize the execution environment and establish a global JNDI name for a remote interface of an EJB. In the same way, Oracle WebLogic provides you other tools to link an EJB reference to a specific target EJB using a global JNDI name.

In the examples seen so far, we've always linked an EJB reference to a target using the portable mechanisms provided by the EJB v. 3.0 Specification. If you need to establish a target to a remote EJB outside the scope of your application, you can use the WebLogic-specific deployment descriptor of the module that acts as the EJB client. In the case of the Java EE module we've used so far, you can use JDeveloper to add the WebLogic-specific deployment descriptor called weblogic.xml. If you're using other IDEs, the skeleton of this file is the following (as of Oracle WebLogic 10.3):

<?xml version = '1.0' encoding = 'UTF-8'?>
<weblogic-web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.bea.com/ns/weblogic/weblogic-web-app http://www.bea.com/ns/weblogic/weblogic-web-app/1.0/weblogic-web-app.xsd"
  xmlns="http://www.bea.com/ns/weblogic/weblogic-web-app">
  <ejb-reference-description>
    <ejb-ref-name>ejb/myGlobalRef</ejb-ref-name>
    <jndi-name>global-jndi-name</jndi-name>
  </ejb-reference-description>
</weblogic-web-app>

This weblogic.xml deployment descriptor links the EJB reference ejb/myGlobalRef with the object stored with the global JNDI name global-jndi-name, that is the name we've specified in the weblogic-ejb-jar.xml file described in the previous section. This reference will be declared as usual with a @EJB annotation or in the standard deployment descriptor (in this case, the web.xml file).

If you're using JDeveloper, a GUI will help you build the WebLogic-specific deployment descriptor. The GUI is well done and is also able to scan you module's EJB references and let them choose from a list when customizing them.

Next Steps

In this part we've learnt how to use WebLogic-specific tools in the case we had to link an EJB reference to an EJB that lives outsides our application using a global JNDI name. In the following part (coming soon) we'll see how the EJB v. 3.1 Specification has (finally) filled this gap defining portable JNDI names. With EJB v. 3.1 you'll be able to build modular Java EE application that reuse components of one another without the need to rely on non-portable mechanisms any longer.







EJB Programmatic Lookup

Table of Contents


In our previous post we learnt about EJB references and EJB injection. Even though EJB injection is a powerful container facility that eases modular application development, sometimes performing a programmatic EJB lookup is instead desirable.

Let's suppose, for example, that a set of distinct EJBs implement a common strategy, defined by a common business interface. Depending on the result of some choice algorithm (such as a business rule), a different strategy is chosen and hence a different EJB will be executed in the scope of a business process. In such a scenario the target EJB cannot be chosen at injection time since annotation elements (such as @EJB's) are defined at compilation time and deployment descriptors are defined at deployment time. The only solution to this problem is a programmatic JNDI lookup.

The same mechanisms described in the previous posts will apply. EJB references will be declared and linked against a name in your application private namespace using the @EJB annotation or the corresponding elements of your Java EE module deployment descriptor.

Lookup in the Application Private Namespace

The portable way to establish an indirection level between the namespace used in your lookup code and the target EJBs is using your application private namespace. This kind of indirection level is quite common in the Java EE platform: not only it is used for EJB references but for all sorts of resource references such as JDBC data sources, JMS queues, JavaMail sessions, etc.

In the case of EJBs, as seen in our previous post, you simply define a private name used by lookup and injection code of your application. This is a private application-scoped name and is a subelement of the java:comp/env JNDI entry. With the aid of the @EJB annotation and of the deployment descriptors, you can establish a link between this name and a target EJB. The only difference is that instead of relying on the container to inject a reference into a component of yours, your application algorithms will choose the appropriate EJB and will look it up dynamically.

As we've seen in part 2 of this series, the @EJB annotation can be used at type, method and field level to declare a reference to an EJB and, optionally, to link it to the target bean without the need of writing any deployment descriptor code.

If the case of dynamic programmatic JNDI lookup, instead of annotating a field (or a property) as an injection target, you can annotate a class (such as a Servlet) to establish a reference to an EJB. In the following example we'll see how to do it with both the @EJB annotation and the deployment descriptor.

Declaring a Reference to an EJB

In the test servlet we've used throughout our previous posts, we can use the @EJB annotation at class level to declare a reference to an EJB in the private name ejb/ejbLocalRef:

@EJB(name = "ejb/ejbLocalRef",
     beanInterface = es.reacts.SessionTest0Local.class,
     beanName = "EJBServer1.jar#SessionTest1")
public class ServletTest1 extends HttpServlet {
  [...]
}

The annotation in the previous example is functionally equivalent to the following deployment descriptor (in this case, the web.xml file) fragment:

<ejb-local-ref>
  <ejb-ref-name>ejb/ejbLocalRef</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <local>es.reacts.SessionTest0Local</local>
  <ejb-link>EJBServer1.jar#SessionTest1</ejb-link>
</ejb-local-ref>

The most important difference between the @EJB semantics in this example and in the examples in the previous post is that in this case we're providing all of the information required to establish the reference and the link to the target EJB without injecting nor even relying on information coming from the injection target (such as the beanInterface).

Although the annotation is applied at a class level, it is effectively equivalent to adding the corresponding deployment descriptor elements and, therefore, the declared reference will be available throughout your Java EE module. In this case, any other servlet in your Java EE web module will be able to inject or lookup the very same EJB referenced by the ejb/ejbLocalRef name:

@EJB(name = "ejb/ejbLocalRef")
SessionTest0Local lc4;

Additional "plumbing" here is not necessary since the reference declaration contains all of the information that is required to resolve the target EJB.

EJB Programmatic Lookup

Since a reference has been declared and linked, our code is now able to make a JNDI lookup and retrieve a reference to the desired business interface of our target EJB. The JNDI lookup code is the good ole lookup code we're used to (with a little difference we'll point out later):

InitialContext ctx = new InitialContext();
Object obj = ctx.lookup("java:comp/env/ejb/ejbLocalRef");
if (obj instanceof SessionTest0Local) {
  SessionTest0Local lc = (SessionTest0Local) obj;
  [...]
}

(* Please note that the previous fragment has been stripped of the required exception handling code.)

The good news with EJB 3.0 is that you don't need to narrow the reference using the PortableRemoteObject.narrow() method as was required by the EJB v. 2.1 Specification. In the example code we can directly test the reference with the instanceof operator and use a Java native cast to set a SessionTest0Local reference.

There's absolutely no difference between looking up local and remote business interfaces. Only in the case you rely on the deployment descriptor, the declaration and the linking of the EJB references will be performed using an <ejb-ref/> or an <ejb-local-ref/> according to the EJB business interface type. As far as it concerns your application, the lookup code will be identical.

Patterns

In scenarios in which you don't use EJB injection and rely on lookup instead, there are both advantages and disadvantages in using annotations or the deployment descriptors to declare and link EJB references.

The advantages of annotations is that they're easier to write and use than the corresponding deployment descriptors elements. Moreover, as far as it concerns my experience, IDE support for code autocompletion might be a better tool than some deployment descriptor editors which are "obscure", at best (with notable exceptions such as Oracle JDeveloper's and NetBeans'.)

The advantage of the deployment descriptor is that it can centralize resource references declarations. If the same EJB reference is used throughout the code of your Java EE module and is not confined to a single class, a best option is using the deployment descriptor to declare and link the EJB (and other resource) reference and avoid using annotations at all. This is a design choice that has to be taken carefully. In the uses cases in which a lookup is desirable, chances are that EJB linking might be performed in the application assembly and deployment stages. Having references well documented and declared in a central repository might still be preferable instead of having @EJB annotation scattered throughout your code and the task of the deployer might get considerably eased.

Next Steps

In the following part we'll see how application server-specific mechanisms will help us build modular Java EE applications and link EJB references to EJB remote business interfaces outside our application. We'll explore the tools provided by a leading Java EE compliant application server: Oracle WebLogic.


Thursday, September 23, 2010

Basic EJB References, Injection and Lookup

Table of Contents


In the first part of this series we've introduced the mechanisms provided by the Enterprise JavaBeans v. 3.0 Specification to define EJB components, declare a reference to an EJB and wiring them up both by dependency injection or programmatic JNDI lookup.

In this blog post we'll examine some basic examples to understand how to use the EJB API.

A Basic EJB

An EJB is basically a POJO with some extra EJB metadata. The required metadata to deploy it as an EJB component can be provided both by using the EJB annotations or by the standard deployment descriptor. The following class implements a very basic stateless session EJB:

package es.reacts;

import javax.ejb.Stateless;

@Stateless(name = "UniqueLocalSessionEJB")
public class UniqueLocalSessionEJBBean implements UniqueLocalBusinessInterface {
    public UniqueLocalSessionEJBBean() {
    }
    
    public String sayLocalHello() {
      return this.getClass().getName() + "::" + "Local hello.";
    }
}

As you may recollect from our previous blog post, the @Stateless annotation is used to define a stateless session bean. The optional name element is used to define the session bean name. This element is analogous to the <ejb-name/> element of the standard deployment descriptor. This element defaults to the unqualified name of the bean class (UniqueLocalSessionEJBBean in the example above), and the example above uses it to rename the bean to UniqueLocalSessionEJB.

Since we're using the @Stateless annotation, there's no further need to declare the EJB in the deployment descriptor.

In this example, we're assuming that the EJB is packaged in an EJB module that depends on the module containing the definition of its business interface (as explained in the following section.)

Business interfaces

Every EJB implements one or more business interfaces. Business interfaces can be local or remote. The most important differences between the two types of business interfaces can be summarized as follows:
  • Local business interfaces use pass-by-reference semantics for their methods and method invocations cannot cross the JVM boundary. Local business interfaces are available only to callers in the same application and JVM instance of the callee.
  • Remote business interfaces use pass-by-value semantics for their methods and method invocations can cross the JVM boundary. Remote business interfaces are available to callers outside the application of the callee.

In the previous example, the business interface UniqueLocalBusinessInterface is declared as follows:

package es.reacts;

import javax.ejb.Local;

@Local
public interface UniqueLocalBusinessInterface {
    String sayLocalHello();
}

In the EJB v. 3.0 world a business interface is just a plain old Java interface annotated with either the @Local or @Remote annotation.

Packaging Business Interfaces

In this example we're assuming that the EJB business interface is packaged in a JAR file that the EJB module depends on. Since EJB clients only depend on an EJB business interface, it's a good practice to package the business interfaces in a separate library in order to ease the interface distribution and to decouple them from their implementations.

Injecting an EJB into a Java Servlet

Now that we've defined an EJB, we're ready to use it from a servlet in a Java EE web module. Assuming that there's only one EJB implemented the UniqueLocalBusinessInterface in our application, we can inject it using an empty @EJB annotation:

package es.reacts;

import java.io.IOException;
import java.io.PrintWriter;

import javax.ejb.EJB;

import javax.servlet.*;
import javax.servlet.http.*;

public class ServletTest1 extends HttpServlet {
  @EJB
  UniqueLocalBusinessInterface lc;

  public void doGet(HttpServletRequest request,
    HttpServletResponse response)
    throws ServletException, IOException {
    [...]
    lc.sayLocalHello();
    [...]
  }

The first thing to note is that the EJB is injected into our servlet by the application server since the bean interface alone is sufficient to identify the target EJB. In this case the beanInterface element of the @EJB annotation takes on its default value, as explained in our previous post, that is the type of the injected field: UniqueLocalBusinessInterface. Since there's just one EJB in the application that implements this business interface, the lc field of the servlet is injected with a reference to an instance of such a class.

The second thing worth pointing out is that we're injecting an EJB into a servlet field safely because the EJB is stateless. Since servlet are stateless by default, you should not inject stateful resources into servlet fields properties otherwise you may run into concurrency-related problems. If you needed to use a stateful EJB into a servlet, you should retrieve a reference by programmatic JNDI lookup since that will guarantee that a new instance is returned for every lookup operation.

Let's deploy and run our application and we'll see that the servlet is injected its target EJB and the method invocation to the sayLocalHello() method of its business interface is carried out correctly.

If we wanted to inject a reference to a remote interface the client code would not be affected. If you try and change the UniqueLocalBusinessInterface from @Local to @Remote, you'll see that the servlet sees no change and continues to work correctly.

What Happens If More Than One EJB Implements the Same Interface?

Let's suppose that the we add another EJB in our EJB module in this application that implements the same interface as the previous one, UniqueLocalBusinessInterface. In this case, since the bean interface is not sufficient any longer to determine the target bean for injection, you'll be returned an error. Deploying such an application in the WebLogic Application Server, for example, results in the following error being thrown:

[08:46:25 PM] Caused by: weblogic.deployment.EnvironmentException: [J2EE:160199]Error resolving ejb-ref 'es.reacts.ServletTest1/lc1' from module 'WebTest0' of application 'EJBTestApp'. The ejb-ref does not have an ejb-link and the JNDI name of the target bean has not been specified. Attempts to automatically link the ejb-ref to its target bean failed because multiple EJBs in the application were found to implement the 'es.reacts.UniqueLocalBusinessInterface' interface. Please specify a qualified ejb-link for this ejb-ref to indicate which EJB is the target of this ejb-ref.

Injecting a Reference to a Specific EJB Instance

To solve the problem occurred in the previous section we need to provide the application server with the required information to identify the target EJB. As explained in our previous post, we can use the following two methods:
  • Either we use the name element of the @EJB annotation (or the corresponding <ejb-ref-name/> element of the deployment descriptor) to declare an EJB reference in the private namespace of the application and link it to the target bean using the deployment descriptor.
  • Or we use the beanName element of the @EJB annotation (or the corresponding <ejb-link/> element of the deployment descriptor) to do it directly in our code.

Mapping an EJB into the Private Namespace

Using the first method we'll end up with the following code in our servlet:

@EJB(name = "ejb/bean-name")
UniqueLocalBusinessInterface lc;

and the following element in the deployment descriptor (web.xml) of our Java EE web module that acts as an EJB client:

<ejb-local-ref>
  <ejb-ref-name>ejb/bean-name</ejb-ref-name>
  <ejb-ref-type>Session</ejb-ref-type>
  <local>es.reacts.UniqueLocalBusinessInterface</local>
  <ejb-link>UniqueLocalSessionEJB</ejb-link>
</ejb-local-ref>

The <ejb-link/> element contains the bean name that we defined at the beginning of our example with the annotation:

@Stateless(name = "UniqueLocalSessionEJB")

in the EJB implementation class.

Please note that, in this example, we used the @EJB name element explicitely but we could have established the link using its default value. The default value of the name element is:

[qualified class name]/[property or field name]

that, in this case, would be:

es.reacts.ServletTest1/lc

The disadvantage of using the default auto-generated name together with EJB linking using <ejb-link/> is that every time you refactor your code you'll have to check the deployment descriptors. Although developers sometimes think otherwise, the Java EE Specification defines some other roles such as the assambler and the deployer. In large corporate environments, it's not uncommon for such profiles to override developers' annotations to "plumb" the components used by the applications. Annotation override is one of the reasons why standard deployment descriptors still exist. This is especially true when references are to remote components, inside or outside your application. For this reason I suggest you do not rely on auto-generated names and use custom well documented names instead.

Linking an EJB to a Reference in the Private Namespace

The second method provides a direct way to link the reference to its target bean using the beanName element of the @EJB annotation. The servlet code will use the following EJB reference:

@EJB(beanName = "UniqueLocalSessionEJB")
UniqueLocalBusinessInterface lc;

and we need no additional information in the deployment descriptor.

Although this method allows the developer to link a reference to an EJB without relying on the deployment descriptor, the suggestion given at the end of the previous section is still valid. Remember that annotations can be overridden at deployment time! Do not link an EJB to a reference if you know beforehand that such a reference is eligible for override. In such cases, prefer assigning a name to the reference instead, as explained in the previous section.

Next Steps

In the following post we'll make a quick overview of how the EJB 3.0 API can be used to perform EJB programmatic lookups.