Hot questions for Using Enterprise JavaBeans in openejb

Question:

I have the following EJB's:

PersonService.java

@Local
public interface PersonService {
    long countPersons();
}

PersonServiceImpl.java

@Stateless
public class PersonServiceImpl implements PersonService {

    @EJB
    private RemotePersonService remotePersonService;

    @Override
    public long countPersons() {
         return remotePersonService.getAllPersons().size();
    }
}

RemotePersonService.java

@Local
public interface RemotePersonService {
    List<Person> getAllPersons();
}

RemotePersonServiceImpl.Java

@Stateless
public class RemotePersonServiceImpl {
    @Override
    public List<Person> getAllPersons() {
        // Here, I normally call a remote webservice, but this is for the purpose of this question
        List<Person> results = new ArrayList<Person>();
        results.add(new Person("John"));
        return results;
    }
}

And here are my tests

AbstractTest.java

public abstract class AbstractTest {

    private InitialContext context;

    @BeforeClass(alwaysRun = true)
    public void setUp() throws Exception {
        System.setProperty("java.naming.factory.initial", "org.apache.openejb.client.LocalInitialContextFactory");

        Properties properties = new Properties();
        properties.load(getClass().getResourceAsStream("/unittest-jndi.properties"));

        context = new InitialContext(properties);
        context.bind("inject", this);

    }

    @AfterClass(alwaysRun = true)
    public void tearDown() throws Exception {
        if (context != null) {
            context.close();
        }
    }
}

PersonServiceTest.java

@LocalClient
public class PersonServiceTest extends AbstractTest {

    @EJB
    private PersonService personService;

    @Test
    public void testPersonService() {
        long count = personService.countPersons();

        Assert.assertEquals(count, 1l);
    }
}

Now, want I want to do is replace the RemotePersonService implementation in PersonServiceImpl.java by a mock using Mockito, and still have the same call in my testPersonService method.

I tried that:

PersonServiceTest.java

@LocalClient
public class PersonServiceTest extends AbstractTest {

    @Mock
    private RemotePersonService remotePersonService;

    @EJB
    @InjectMocks
    private PersonService personService;

    @BeforeMethod(alwaysRun = true)
    public void setUpMocks() {
        MockitoAnnotations.initMocks(this);

        List<Person> customResults = new ArrayList<Person>();
        customResults.add(new Person("Alice"));
        customResults.add(new Person("Bob"));

        Mockito.when(remotePersonService.getAllPersons()).thenReturn(customResults);
    }

    @Test
    public void testPersonService() {
        long count = personService.countPersons();

        Assert.assertEquals(count, 2l);
    }
}

But this doesn't work. The @Mock RemotePersonService is not injected in the PersonService, and the true EJB is still used.

How can I make this work ?


Answer:

Don't use annotations for your tests. Have a constructor that will wire in all your dependencies.

@Stateless
public class PersonServiceImpl implements PersonService {

    @EJB
    private RemotePersonService remotePersonService;

    // Let your test instantiate a mock service and wire it into your test instance using this constructor.
    public PersonServiceImpl(RemotePersonService rps) {
        this.remotePersonService = rps;
    }

    @Override
    public long countPersons() {
         return remotePersonService.getAllPersons().size();
    }
}

Create mocks and pass them to it. Your test might look like this:

@LocalClient
public class PersonServiceTest extends AbstractTest {

    @Test
    public void testPersonService() {
        RemotePersonService mockRemotePersonService = Mockito.mock(RemotePersonService.class);
        List<Person> customResults = new ArrayList<Person>();
        customResults.add(new Person("Alice"));
        customResults.add(new Person("Bob"));
              Mockito.when(mockRemotePersonService.getAllPersons()).thenReturn(customResults);
        PersonService personService = new PersonServiceImpl(mockRemotePersonService);
        long count = personService.countPersons();    
        Assert.assertEquals(count, 2l);
    }
}

Question:

I'm trying to integrate TomEE 1.7.2 to an embedded tomcat server instance along with OpenEJB 4.7.2 and OWB 1.2.7. I have created an OSGi bundle which contains them and CXF.

I'm trying to test the TomEE integration using the ejb-example which is provided with TomEE. I tested the JNDI dump by running it on my server and standard TomEE distribution.

In my server it is missing all the openejb stuffs

env/openejb=
env/openejb/Resource=
env/openejb/Resource/context.xml=
env/openejb/Resource/context.xml/resource=[ResourceBean ContextResource]
env/openejb/Resource/ejb-examples-1.0.0-SNAPSHOT=
env/openejb/Resource/ejb-examples-1.0.0-SNAPSHOT/context.xml=
env/openejb/Resource/ejb-examples-1.0.0-SNAPSHOT/context.xml/resource=[ResourceBean ContextResource]
env/openejb/Resource/ejb-examples-1.0.0-SNAPSHOT/web.xml=
env/openejb/Resource/ejb-examples-1.0.0-SNAPSHOT/web.xml/resource-env-ref=[ResourceBean ContextResourceEnvRef]
env/openejb/Resource/ejb-examples-1.0.0-SNAPSHOT/web.xml/resource-ref=[ResourceBean ContextResourceRef]
env/openejb/Resource/web.xml=
env/openejb/Resource/web.xml/resource-env-ref=[ResourceBean ContextResourceEnvRef] 
  env/openejb/Resource/web.xml/resource-ref=[ResourceBean ContextResourceRef] 

And in the annotated example datasource is null.

What could be the reasons for these issues? Is it because may be configurations aren't pick up correctly?


Answer:

Just to not let this thread pending here are the outcomes http://tomee-openejb.979440.n4.nabble.com/ejb-example-is-not-working-properly-when-trying-to-integrate-TomEE-1-7-2-Integration-with-embedded-Tt-td4675013.html

Question:

I'm testing my application using junit and OpenEJB container to provide context for my beans. It's finding all beans from classpath and starting them. Unfortunatelly there are some beans I would like to remove from context, so I can replace those beans with other implementations, mocking certain functionalities.

I'm aware of openejb.deployments.classpath.exclude property. I even tried to use it as follows properties.put("openejb.deployments.classpath.exclude", ".*/CommonCache.*"); as it was sugested in this SO question.

OpenEJB sees this property but bean is still starting as shown in logs below.

Using 'openejb.deployments.classpath.exclude=.*/CommonCacheBean.*'
Auto-deploying ejb CommonCacheBean: EjbDeployment(deployment-id=CommonCacheBean)
Jndi(name="java:global/ejbs/CommonCacheBean!my.package.ICommonCache")
Created Ejb(deployment-id=CommonCacheBean, ejb-name=CommonCacheBean, container=Default Stateless Container)

So there is my question. Is there a way to exclude single bean(s) from OpenEJB context? It doesn't matter to me if it will be achieved this config way or by manual operations in java code.


Answer:

If anyone is interested i didn't manage to remove beans from context. Although there is an unbind() method in context it does not seem to work on OpenEJB context. I succeded in replacing beans manually with rebind() but it was too late because they were already injected into another beans.

They way i solved my problem was by using annotaion @Alternative on mock implementation. I also had to add entries in beans.xml to show container those beans and change the way I inject them from @EJB to @Inject.

Question:

I've just created a simple Enterprise Application Project in Eclipse with web module and EJB3 module.

This is my EJB:

@Stateless
public class MyBean implements MyBeanRemote {
    public String getGreeting(){return "Hello World!";}
}

This is my Remote Business Interface:

@Remote
public interface MyBeanRemote {
    String getGreeting();
}

This is my Servlet:

@WebServlet("/MyServlet")
public class MyServlet extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        MyBeanRemote myBean = null;

        try {
            myBean=(MyBeanRemote) new InitialContext().lookup("XXX");                //1
        } catch (NamingException e) {e.printStackTrace();}

        response.getWriter().append(myBean.getGreeting());
    }
}

Bean injection works like a charm, but I'm not being able to successfully perform a JDNI lookup: what should be inserted at line //1 instead of XXX? I've tried any possible combination (well, clearly missing the right one...)

As far as I understand, Geronimo uses OpenEJB to map name to resources, and at this page you can read that

The default JNDI name is in the following format: {deploymentId}{interfaceType.annotationName}

so I thought it was just MyBeanRemote, but that's not working... What am I missing? I've spent my last hours with unsuccessful attempts and reading similar answers here on SO.

This is my openejb.jar.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<ejb:openejb-jar xmlns:app="http://geronimo.apache.org/xml/ns/j2ee/application-2.0" xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0" xmlns:client="http://geronimo.apache.org/xml/ns/j2ee/application-client-2.0" xmlns:conn="http://geronimo.apache.org/xml/ns/j2ee/connector-1.2" xmlns:dep="http://geronimo.apache.org/xml/ns/deployment-1.2" xmlns:ejb="http://openejb.apache.org/xml/ns/openejb-jar-2.2" xmlns:jaspi="http://geronimo.apache.org/xml/ns/geronimo-jaspi" xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-2.0" xmlns:name="http://geronimo.apache.org/xml/ns/naming-1.2" xmlns:pers="http://java.sun.com/xml/ns/persistence" xmlns:pkgen="http://openejb.apache.org/xml/ns/pkgen-2.1" xmlns:sec="http://geronimo.apache.org/xml/ns/security-2.0" xmlns:web="http://geronimo.apache.org/xml/ns/j2ee/web-2.0.1">
    <dep:environment>
        <dep:moduleId>
            <dep:groupId>default</dep:groupId>
            <dep:artifactId>MyProjectEJB</dep:artifactId>
            <dep:version>1.0</dep:version>
            <dep:type>car</dep:type>
        </dep:moduleId>
    </dep:environment>
</ejb:openejb-jar>

as created by the wizard.


Answer:

As user achabahe suggested, and with the help of these question/answers, I printed my whole JNDI tree:

String getJNDITree() {
    Context context=null;
    try {
        context = new InitialContext();
    } catch (NamingException e1) {e1.printStackTrace();}

    List<String>roots=Arrays.asList("java:comp","java:module","java:app","java:global");

    StringBuilder tree=new StringBuilder();
    for(String root:roots)
        recursiveDeepVisit(context,root,"",tree);   

    return tree.toString()+"\n";
}

void recursiveDeepVisit(Context context,String path,String prefix,StringBuilder tree){
    tree.append("\n"+prefix+path);
    try {
        NamingEnumeration<NameClassPair> list = context.list(path);
        while (list.hasMore()) {
            String innerPath = path+"/"+list.next().getName();
            recursiveDeepVisit(context, innerPath, "    "+prefix,tree);
        }
    } catch (Exception e) {}
}

getting this output

java:
    java:app
        java:app/env
        java:app/MyProjectEJB
            java:app/MyProjectEJB/MyBean!com.dev.bean.MyBeanRemote
            java:app/MyProjectEJB/MyBean
        java:app/AppName
    java:comp
        java:comp/UserTransaction
        java:comp/HandleDelegate
        java:comp/ValidatorFactory
        java:comp/TransactionManager
        java:comp/Bundle
        java:comp/TransactionSynchronizationRegistry
        java:comp/env
        java:comp/ORB
        java:comp/Validator
        java:comp/BundleContext
    java:TransactionManager
    java:TransactionSynchronizationRegistry
    java:module
        java:module/ModuleName
        java:module/env
    java:global
        java:global/MyProject
            java:global/MyProject/MyProjectEJB
                java:global/MyProject/MyProjectEJB/MyBean!com.dev.bean.MyBeanRemote
                java:global/MyProject/MyProjectEJB/MyBean
        java:global/geronimo-mejb-3.0.1
            java:global/geronimo-mejb-3.0.1/MEJB
            java:global/geronimo-mejb-3.0.1/ejb
                java:global/geronimo-mejb-3.0.1/ejb/mgmt
                    java:global/geronimo-mejb-3.0.1/ejb/mgmt/MEJB
                    java:global/geronimo-mejb-3.0.1/ejb/mgmt/MEJB!javax.management.j2ee.ManagementHome
            java:global/geronimo-mejb-3.0.1/MEJB!javax.management.j2ee.ManagementHome
        java:global/env

It turns out that

MyBeanRemote myBean=(MyBeanRemote) new InitialContext().lookup("java:app/MyProjectEJB/MyBean!com.dev.bean.MyBeanRemote"); 

is the solution I was looking for.