Embeddable ejb container

 - by joerg

Since EJB 3.1 a new embeddable API is available for executing EJBs. In the following article I describe a simple setup to make a container available in a JUnit Test.

First, create a standard maven project:

$ mvn -DarchetypeArtifactId=ejb-javaee6 archetype:generate

and add a additional dependency for the embeddable glassfish:

<dependency>
    <groupId>org.glassfish.extras</groupId>
    <artifactId>glassfish-embedded-all</artifactId>
    <version>3.1-SNAPSHOT</version>
    <scope>test</scope>
</dependency>

Now write your EJB:

@Stateless
public class Foo {
    public String cheers()
      throws ClassNotFoundException, InstantiationException,
        IllegalAccessException {
      return ((Class<?>) new BeerLoader().loadClass("BeerSong")).newInstance().toString();
    }
 
    private class BeerLoader extends ClassLoader {
      // assembled by tilo dickopp
      private static final String BEER_SONG = " #§$%MBVC?*9@QW68Q986@9                                                                   " +
        " @6######%§##%@#9##§§##§9                                                                 " +
        "   #C##§@#9#   ##$##§9#?#                                                                 " +
        "   #§Q#?##§W    #?##§6#9#                                                                 " +
        "   #§8##$##9    ###$##$§#         ?##$$#?##           $%#?##$M#?         ##$B#?#  #$V#?#  " +
        "   #$C#?##$?    #9###$##       $*#C##$9#9##$@       ##$Q#9##$W##$6   #C##$8#§### V%QV*V6V " +
        "   *CM%6#§###%$?$*BV#§#       ##MM%V8   VMVB#§#   ##?CMV8B  %CMC$V*  V6VC#§##§M$?$*MQV9V§ " +
        "   CVV§$8VQV§V6VC$8B%CMC$    V*V6VC%    @#Q##§B  ##§V#§##    §CV9V§C   VV§$8VQV§V6VC$8B%C " +
        "   MC$V*V6VCM$CBV*VQVMVBC$  #§##%WC@%#$QV%V?V8V* V%VB$Q%#$%V6V8$#VWV   8C$VB$#V$  V8CMCM  " +
        "   VQVBC%CQ%     §$%%§$#V$V 8CMCMVQVBCQ%§%QC@%#  CW$#V$V8CMCMVQVBC%C   W$#V8VV$#          " +
        "   V$VBVBC$#     §###§V6#§# ##§M6#C##            %##Q##%§              ##%$#Q##           " +
        "   %%##%M#§#     ##6$#V8V6$  #CMV?VB$#           CCV§VQVQ$             Q$##§###$          " +
        "  $6#9#§##$WC   @%#$QV%V?V    8V*V%VB$Q%#$%MCV8   $#CMV8$#CMV?VB$#C%   CMV8C$VB$          " +
        "  #V§V6VM$#V$CBC*$#C%V8VWV      B$#VWV8C$VB#§##$    VCQ%#%QBMV§V@VB$  #V8V6VB$#VMV8C      " +
        " CV6$#V§V6VM$#C#V§C%C%$#          V*CM$#V§C$V         8CBV6VMCW$Q$    ##§###$C@%##§#      " +
        "                                                                                          " +
        " ##$C@%§#§###8$#V8V6$#C                                                                   " +
        " MV?VB$#CCV§VQVQ$6#9#9#Q#                                                                 " +
        "   #§?##§*#§   ##§#V9V§CV                                                                 " +
        "   V§$8VQV§V    6VC$8M8V$                                                                 " +
        "   V9VBV%CM#    C##%B#Q##         %V##%C#C#           #%?#Q##%*#         #%9#§##  #?M$VB  " +
        "   VBC$B%V8V    6VC#§##§       #V9V§CVV§$8VQV       §V6VC$8B%CMC$V   *V6VC#§###C C$VBC#VQ " +
        "   V§V%VB#§##MM$?MQV9V§       CVV§$8V   QV§V6VC   $8M%V?V§  C$B%VBC  §CBVBV6V%VB%@MQV9V§C " +
        "   VV§$8VQV§V6VC$8M%V?V§C    $B%VBC§    CBVBV6V  %VB%@$*M    QV9V§CV   V§$8VQV§V6VC$8B%CM " +
        "   C$V*V6VC%@#§###VV§C#C#V  BV6VM#§##$W$?MQV9V§C VV§$8VQV§V6VC$8B%CM   C$V*V6VC%  @$*MQV  " +
        "   9V§CVV§$8     VQV§V6VC$8 B%CMC$V*V6VCM$CBV*V  QVMVBC$%@#§##§§V9V§   CVV§$8VQV          " +
        "   §V6VC$8M*     V6CMVBVCVB C$#§###CC            VV§VQCBV              BM8VV#§#           " +
        "   #§V$?M*$*     MQV9V§CVV§  $8VQV§V6V           C$8M*V6CM             VBVCVBC$%          " +
        "  @#§##§CV9V§   CVV§$8CMVB    C?CM$8MWVBC%C%V§V   CVBMVV8C$VWV§CM#§#   ##VVVV8C$          " +
        "  VWV§CM#§##%*$?MQV9V§CVV§      $8VQV§V6VC$8B%CM    C$V*V6VC%@B@MQV9  V§CVV§$8VQV§V6      " +
        " VC$8M8V$V9VBV%CM%@$*MQV          9V§CVV§$8VQ         V§V6VC$8B%CM    C$V*V6VC%@##$§      " +
        "                                                                                          " +
        " ##§M##§§###########$##                                                                   " +
        " #§##§B##§V###§##§C######                                                                 " +
        "   §§###§###   §#######B$                                                                 " +
        "   9@C###§@§    #########                                                                 " +
        "   ##§##§?##    §*###§##§         C######?V           ###C###M##         ####C9@  @###$B  " +
        "   *@C###%MQ    §#V%%W§Q       *@##V*§$#MM6$@       @@###$B*@C###%   $W§$#B§$#V@ V###C@V# " +
        "   ##?§$#*@V###?$W@V###       ?§$#9@V   ###?§$#   @@V###?§  $#Q@V##  #?$W§$#W§$#6@V###C@V " +
        "   ###?§$#8@V###?@V##§##B    @W##§§B    *#%§Q@?  ##§$B%B*    #M§Q§#V   %V#§#VMC#@?##§$B%@ " +
        "   ?##§%@V###?BC?M#$889C88  **$@@V##§#@######### ####C§$*M§QCC@@#V*?   M%V*Q9?C%  B§Q%CM  " +
        "   @C9V@Q8C$     *@6#M9B§%6 %@Q*§B6#C?CQ?*9WQ%9  9QVC#89B§#*6CQ%#*B§   V?9$9QMV§          " +
        "   M@Q@V6V88     8V9§@W96M$ §??V8M@C%            %**MQ@§*              $§VC%$@*           " +
        "   C§8?M98QC     #M$9W%$6B9  VV$$§WCMQ           $%#W8§#QB             ?W8C?$W9@          " +
        "  96C9@V?%$*6   89BVWV@W8@    VM$M#CC8V$@M$6M*%   V#$MC6W#§%%%*8#CM@   M*B@%9$W?          " +
        "  $9$99M$?*8B$VM8CC§CW6W@*      %W%CC?@#§?B8@W%9    ?*%$9B%8Q*§BQ$Q9  6@V9#?V98C$M$M      " +
        " M@%??C88M$86M$QV§WC?B?V          Q%§B8§#%?MQ         B6§BM?B9@#$%    WQMW?BBBB?#§§6      ";
 
      @Override
      public Class<?> loadClass(String name) throws ClassNotFoundException {
        String alphabet = "";
        byte[] code = new byte[946];
        int i = 0;
        int j = 0;
        boolean firstNibble = false;
 
        while (i < 946) {
          while (BEER_SONG.charAt(j) == ' ')
            j++;
 
          if (alphabet.length() < 16) {
            alphabet += BEER_SONG.charAt(j++);
          } else if (firstNibble = !firstNibble) {
            code[i] = (byte) (alphabet.indexOf(BEER_SONG.charAt(j++)) * 16);
          } else {
            code[i++] += alphabet.indexOf(BEER_SONG.charAt(j++));
          }
        }
 
        return name.equals("BeerSong") ? defineClass(name, code, 0, 946)
                                         : findSystemClass(name);
      }
    }
}

Now we are able to write our JUnit test. The properties are necessary because creating the container just with createEJBContainer() is only searching for ejb jars within the classpath. So we are putting our search path for modules as described below:

@Test
public void shouldSing() throws Exception {
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put(EJBContainer.MODULES, new File("target/classes"));
 
    // now create the runtime container
    EJBContainer ec = EJBContainer.createEJBContainer(properties);
    Context ctx = ec.getContext();
 
    // lookup the bean
    Foo bar = (Foo)ctx.lookup("java:global/classes/Foo");
 
   // call the ejb from container
   assert bar.cheers().startsWith("99 bottles of beer");
}

The portable JNDI name for the EJB is “java:global/classes/Foo” and the fully-qualified notation is “java:global/classes/Foo!com.seventhbit.Foo”. The portable name is constructed in the global namespace + the unqualified name of the directory + the bean name. The latter is created by adding the fully-qualified name of the interface to the former.

Happy testing.