Embedded and Remotable has been edited by David Blevins (Nov 02, 2007).

(View changes)

Content:

Overview

This example shows how to use OpenEJB3's remoting capabilities in an embedded scenario.

The basic recipe is the same for a standard embedded scenario but with these added ingreditents:

  • openejb.embedded.remotable property
  • openejb-ejbd jar

While creating the InitialContext, pass in the openejb.embedded.remotable property with the value of "true". When this is seen by the LocalInitialContextFactory, it will boot up the Server ServiceManager in the VM which will in turn look for ServerServices in the classpath.

Provided you have the openejb-ejbd jar in your classpath along with it's dependencies (openejb-server, openejb-client, openejb-core), then those services will be brought online and remote clients will be able to connect into your vm and invoke beans.

If you want to add more ServerServices such as the http version of the ejbd protocol you'd simply add the openejb-httpejbd jar to your classpath. A number of ServerServices are available currently:

  • openejb-ejbd
  • openejb-http
  • openejb-telnet
  • openejb-derbynet
  • openejb-hsql
  • openejb-activemq

The source for this example can be checked out from svn:

$ svn co http://svn.apache.org/repos/asf/incubator/openejb/trunk/openejb3/examples/telephone-stateful/

To run the example simply type:

$ cd telephone-stateful

$ mvn clean install

If your goal is simply to unit test beans with remote interfaces, this is not the right example for you. The LocalInitialContextFactory completely supports remote interfaces and all spec required pass-by-value (serialization) semantics without the need for network sockets. This example shows the use of OpenEJB in an embedded environment where connection outside the
vm is required.

The Code

For this example we have a simple Stateful bean called TelephoneBean as defined below. As a simple way of demonstrating the state we have to methods: speak and listen. You call speak and pass in some text, then you call listen to get your answer.

bean
package org.apache.openejb.examples.telephone;

import javax.ejb.Remote;
import javax.ejb.Stateful;
import java.util.ArrayList;
import java.util.List;

@Remote
@Stateful
public class TelephoneBean implements Telephone {

    private static final String[] answers = {
            "How nice.",
            "Oh, of course.",
            "Interesting.",
            "Really?",
            "No.",
            "Definitely.",
            "I wondered about that.",
            "Good idea.",
            "You don't say!",
    };

    private List<String> conversation = new ArrayList<String>();

    public void speak(String words) {
        conversation.add(words);
    }

    public String listen() {
        if (conversation.size() == 0) {
            return "Nothing has been said";
        }

        String lastThingSaid = conversation.get(conversation.size() - 1);
        return answers[Math.abs(lastThingSaid.hashCode()) % answers.length];
    }
}
business interface
package org.apache.openejb.examples.telephone;

public interface Telephone {

    void speak(String words);

    String listen();
}
EJB3 Notes

The bean class uses the annotation @Remote but does not specify a list of interfaces as is normally required. Per EJB3 rules, if the bean implements exactly one business interface it may use @Remote with no other values and that business interface is then implied to be a remote business interface. The same rule applies to identical usage of @Local.

The critical thing to know is that if you add another interface the rules change and require that you specify both interfaces in the @Remote annotation as in @Remote({Telephone.class, SecondInterface.class}).

Embedding

We're going to embed OpenEJB3 into a plain JUnit TestCase as a simple means of demonstrating the remote capabilities. We'll do the embedding in our test setUp method, then will make two test methods:

  • one for invoking the bean's remote interface via the LocalInitialContextFactory which goes straight against the embedded container system
  • one for invoking the bean's remote interface via the RemoteInitialContextFactory which connects to a Socket and communicates to the embedded container system over the ejbd protocol.

setUp

protected void setUp() throws Exception {
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
    properties.setProperty("openejb.deployments.classpath.include", ".*telephone.*");
    properties.setProperty("openejb.embedded.remotable", "true");
    // Uncomment these properties to change the defaults
    //properties.setProperty("openejb.ejbd.port", "4201");
    //properties.setProperty("openejb.ejbd.bind", "localhost");
    //properties.setProperty("openejb.ejbd.threads", "200");
    //properties.setProperty("openejb.ejbd.disabled", "false");
    //properties.setProperty("openejb.ejbd.only_from", "127.0.0.1,192.168.1.1");

    new InitialContext(properties);
}

LocalInitialContextFactory: making in-vm calls to a remote business interface

public void testTalkOverLocalNetwork() throws Exception {

    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory");
    InitialContext localContext = new InitialContext(properties);

    Telephone telephone = (Telephone) localContext.lookup("TelephoneBeanRemote");

    telephone.speak("Did you know I am talking directly through the embedded container?");

    assertEquals("Interesting.", telephone.listen());


    telephone.speak("Yep, I'm using the bean's remote interface but since the ejb container is embedded " +
            "in the same vm I'm just using the LocalInitialContextFactory.");

    assertEquals("Really?", telephone.listen());


    telephone.speak("Right, you really only have to use the RemoteInitialContextFactory if you're in a different vm.");

    assertEquals("Oh, of course.", telephone.listen());
}

RemoteInitialContextFactory: making networked calls to a remote business interface

This is the part you would want to do in apps that are running a different VM than the one in which the ejb container is embedded. These "client" VMs need only have the the openejb-client jar in their classpath and connect to OpenEJB via the RemoteInitialContextFactory like any other remote EJB client.

public void testTalkOverRemoteNetwork() throws Exception {
    Properties properties = new Properties();
    properties.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.RemoteInitialContextFactory");
    properties.setProperty(Context.PROVIDER_URL, "ejbd://localhost:4201");
    InitialContext remoteContext = new InitialContext(properties);

    Telephone telephone = (Telephone) remoteContext.lookup("TelephoneBeanRemote");

    telephone.speak("Is this a local call?");

    assertEquals("No.", telephone.listen());


    telephone.speak("This would be a lot cooler if I was connecting from another VM then, huh?");

    assertEquals("I wondered about that.", telephone.listen());


    telephone.speak("I suppose I should hangup and call back over the LocalInitialContextFactory.");

    assertEquals("Good idea.", telephone.listen());


    telephone.speak("I'll remember this though in case I ever have to call you accross a network.");

    assertEquals("Definitely.", telephone.listen());
}

Maven setup

Nice thing about maven2 is it has test-only dependencies. This guarantees that non of your runtime code is dependent on any OpenEJB classes.

You need this dep or no clients will be able to connect over the ejbd protocol to the embedded ejb container.

Notice the other examples use 'openejb-core' and this one uses 'openejb-ejbd'. If you wanted to make more protocols available, you simply have to add deps to them and they'll get picked up in the classpath automatically.

<dependency>
  <groupId>org.apache.openejb</groupId>
  <artifactId>openejb-ejbd</artifactId>
  <version>3.0.0-SNAPSHOT</version>
  <scope>test</scope>
</dependency>

Running

Running the example is fairly simple, just run:

$ cd telephone-stateful
$ mvn clean install

Which should create output like the following.

[INFO] Scanning for projects...
[INFO] ----------------------------------------------------------------------------
[INFO] Building OpenEJB :: Examples :: Telephone Stateful Pojo
[INFO]    task-segment: [clean, install]
[INFO] ----------------------------------------------------------------------------
[INFO] [clean:clean]
[INFO] Deleting directory /Users/dblevins/work/openejb3/examples/telephone-stateful/target
[INFO] Deleting directory /Users/dblevins/work/openejb3/examples/telephone-stateful/target/classes
[INFO] Deleting directory /Users/dblevins/work/openejb3/examples/telephone-stateful/target/test-classes
[INFO] [resources:resources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:compile]
Compiling 2 source files to /Users/dblevins/work/openejb3/examples/telephone-stateful/target/classes
[INFO] [resources:testResources]
[INFO] Using default encoding to copy filtered resources.
[INFO] [compiler:testCompile]
Compiling 1 source file to /Users/dblevins/work/openejb3/examples/telephone-stateful/target/test-classes
[INFO] [surefire:test]
[INFO] Surefire report directory: /Users/dblevins/work/openejb3/examples/telephone-stateful/target/surefire-reports

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running org.apache.openejb.examples.telephone.TelephoneTest
log4j:WARN No appenders could be found for logger (OpenEJB).
log4j:WARN Please initialize the log4j system properly.
Apache OpenEJB 3.0-incubating-SNAPSHOT    build: 20061228-01:59
http://incubator.apache.org/openejb
23:04:38,699 INFO  [startup] Found EjbModule in classpath: /Users/dblevins/work/openejb3/examples/telephone-stateful/target/classes
23:04:39,625 WARN  [startup] No ejb-jar.xml found assuming annotated beans present
23:04:39,658 WARN  [OpenEJB] Auto-deploying ejb TelephoneBean: EjbDeployment(deployment-id=TelephoneBean, container-id=Default Stateful Container)
23:04:39,659 WARN  [OpenEJB] Auto-creating a container for bean TelephoneBean: Container(type=STATEFUL, id=Default Stateful Container)
23:04:39,678 INFO  [startup] Loaded Module: /Users/dblevins/work/openejb3/examples/telephone-stateful/target/classes
23:04:40,140 INFO  [startup] OpenEJB ready.
OpenEJB ready.
  ** Starting Services **
  NAME                 IP              PORT  
  ejbd                 0.0.0.0         4201  
  admin thread         0.0.0.0         4200  
-------
Ready!
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.899 sec

Results :
Tests run: 2, Failures: 0, Errors: 0, Skipped: 0

[INFO] [jar:jar]
[INFO] Building jar: /Users/dblevins/work/openejb3/examples/telephone-stateful/target/telephone-stateful-1.0-SNAPSHOT.jar
[INFO] [install:install]
[INFO] Installing /Users/dblevins/work/openejb3/examples/telephone-stateful/target/telephone-stateful-1.0-SNAPSHOT.jar to ...
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESSFUL
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11 seconds
[INFO] Finished at: Thu Dec 28 23:04:41 PST 2006
[INFO] Final Memory: 19M/254M
[INFO] ------------------------------------------------------------------------

Reply via email to